Subscribe vs. Modularization


#1

It’s the first day I removed my autopublish package to get into the “real world”. Now, I am facing some conceptional problems I did not anticipate. Not really a surprise… :wink:

Here’s my scenario:

I have a main object, let’s call it “Thing”. These things are stored in their own collection, Things.

As part of its content, a Thing references a bunch of images. To allow for easy replacement of the actual image storage, I do not store URLs in the Thing itself. Instead, I built an Image Manager package. This image manager maintains it’s own collection named Images, where the actual URLs pointing to the backend storage (S3 based on slingshot in my case) are maintained.

The Thing does not know about this hidden implementation, it just stores references (foreign) keys pointing to images maintained by the media manager, and this media manager is responsible for querying the actual image URLs in S3.

So far, so good.

Now to the subscriptions: As there are quadrillions of potential Things in the database (not yet, but maybe in the future), I cannot subscribe to all of them in the template that shall render a Thing. Instead, I will only subscribe for one Thing at a time. So I place a subscription inside an autorun of the onCreated() method of the Thing’s display template.

This works well for the thing collection itself. But how do I manage the subscriptions for the referenced image objects? Of course, I could place a subscription to the image store collection right inside the same onCreated() method. But this would break separation of concerns, as the Thing should not know anything about the actual implementation of the underlying image store.

So I would rather like to call the Image Manager in the autorun and ask for the URLs matching the referenced keys. There are two ways of doing this that sprang to my mind:

Version 1: Explicit client-server communication using a Method

In the autorun, I call the Image Manager’s API on the client-side. The API method (inside the Image Manager package) picks up this call and hands it over to a Meteor method on the server side which retrieves the image URLs and sends them back via a callback chain.

Version 2: Implicit client-server communication

Instead of calling a Meteor method, the Image Manager API method itself subscribes to the relevant images as soon as it picks up the method call, still on the client side. This would require the autorun dependency management to work across function calls in different packages. I think this is the case, since calling a function in another package is not really different from calling a closure or other function in the originating template, and I think that reactive sources are detected there as well (which is the reason why the very first autorun’s run has to touch all reactive sources at least once).

Which of these two ways would be the best way to do this kind of subscription delegation – or is there a third alternative I am not aware of and that might be much more elegant?

Thanks for reading this long question :slight_smile:


#2

As I dont like handling data in method callbacks, I would subscribe to publication.
And this publication will get array of foreign key handlers as parameter and by using low level publish API send back documents consisting of foreign key and image path pairs. Non-reactively.


#3

If you have normalized your data, your best bet is to use a package to send related data together when the data is published. I personally recommend tmeasday:publish-with-relations, but there are several packages that accomplish this.

As for tying of the data together I’ll shamelessly recommend socialize:base-model… For an implementation of the base-model package that has good documentation, have a look at the code for socialize:friendships


#4

@copleykj: Yes, publish-with-relations might be a choice. But this would also break the separation of concerns to some extent, as the publish method would have to know the details of the relations, and it would have to be defined as a whole on the app level. Or is it possible to “inject” such a dependency / relation from a package (in my case the Images Manager) into the main app’s publication code?

I really like the BaseModel idea! I was about to implement something like that, because I looked for an easy way to link a Collection’s SimpleSchema to my Model objects. One question about this package after having read the documentation: If I create a new Book(), will this Book be initialized with default property values based on the SimpleSchema, if they are not passed to the constructor? This would be awesome, since I use special “blueprint” objects for this and underscore to merge the blueprint with the contructor object. I was planning to use the SimpleSchema’s definition instead. (BTW: There are some typos in the appendSchema docs, and also in the source code it reads attachSchema instead of appendSchena.)


#5

LOL, and this is why I requested documentation review. Thanks for this.

As far as that level of separation goes, I’m not sure but you may be able to to socialize:linkable-model. The documentation on the package is horrible at this point. The socialize:likeable and socialize:commentable packages implement the linkable-model so their code is a good reference at this point. socialize:feed implements commentable and likeable as well… Theses all should have proper documentation in the coming weeks.


#6

Oh, almost forgot the question…

BaseModel doesn’t do this on initialization. I’m sure this could be implemented but currently default values are not added until you call save.


#7

@waldgeist, an you point out where you see the typo’s I’m unable to find them.


#8

@copleykj: I meant the sentence: “Create attach a schema…”