Entrypoints, Services and Dependencies


#1

Hi again,

I have a design question related to my earlier post regarding subscribing to external vendor data and publishing it to RabbitMQ.

I currently have an Entrypoint that handles everything related to the external vendor data. When the Entrypoint is first setup, it reads the list of subscriptions from a database, then open a session with the external data vendor, providing the indicated subscription list. Finally, it repeatedly calls next_data() on the vendor session in an infinite loop, triggering the associated service method with each data point arrival.

As you can guess, this is a poor design because the external vendor session is completely locked inside the Entrypoint. If the subscription list has changed, there is no way for re-subscription to occur without restarting the entire service.

My plan is as follows:
Refactor the code related to the external vendor data into a separate dependency, and bind it up in a DependencyProvider. This dependency will essentially expose 2 methods:

  • next_data() - return the next point published by the external vendor. This method could be called repeatedly by the Entrypoint class, which would now be much smaller, and only concern itself with what to do when a new data point arrives.
  • resubscribe() - refresh the subscription list from the database and resubscribe with the external vendor. This method could be called by a service method listening on a typical RabbitMQ queue. When the appropriate RabbitMQ message arrives, resubscription occurs.

My questions relate are pretty much all variations on, “is this possible / proper design”:

  • Are Entrypoints allowed to have dependencies?
  • Can an Entrypoint and a Service share a dependency?

If there answer to any of these is no, any suggestions you have to achieve a similar goal would be most appreciated.


#2

Hi. Sorry for the late reply to this.

These questions come up quite a lot. It is fairly common to have a service interface that requires more than one external system, and we ought to have a page in the docs that explains how to do it.

You can do this if you’re happy to couple your two extensions together. Extensions can access every other extension via their ServiceContainer (self.container.extensions is the set of all of them) so you can reach into another extension and do what you like with it.

This is possible but I would not say proper design.

Yes, but not the same way that a service can. You can’t declare DependencyProvider on your Entrypoint, because the dependency injection mechanism only works on service instances.

You can use a nested SharedExtension though, which is more or less the same thing but without the injection part. These are generally used to implement something that needs to be shared between multiple top-level extensions. It is an Extension subclass just like Entrypoint and DependencyProvider, and it has the same lifecycle (i.e. started when the service container starts, runs until the container stops).

Good examples of them in the codebase are the WebServer, shared by all HTTP and websocket entrypoints, and the RpcConsumer, shared by all the RPC entrypoints.

Your plan seems good, except that I would use a SharedExtension instead of a DependencyProvider to implement the next_data() and resubscribe() methods.

Additionally I’d probably use another nested extension to consume from the RabbitMQ resubscription queue. This would avoid polluting your service code with message handling that really belongs to your extension.

Hope that helps, please ask if anything is unclear.

Matt.


Dependencies between dependency providers?