servicedomain-driven-designmessagingbounded-contextsdomain-events

DDD: Blocking External Service Calls and Events


I’ve been circling the best approach for integrating an external service into my domain.

There are a number of third-party services that can be used, so we have an Adapter providing a common interface. We only have X number of instances of the service available at any one time (they are expensive to create and destroy) so they are retrieved from a Pool.

The external service is used as part of a long running process. This process will be implemented with a process manager. An entity is scheduled, it then asks the Object Pool for a resource, when the resource is free it performs an action, the result is then recorded, and resource returned to the Pool. Two parts of this process (the request for a free resource and executing an action) can take some time and are blocking calls. So ideally these should be done asynchronously?

This implies the external service needs to raise events when a resource is free, and when a result to the action has been calculated. Every example I see provides guidance on raising events from aggregates. Very little when an external system (in this case a Generic Subdomain?) needs to report events… So my questions:

I’ve struggled to find much concrete guidance out there on the above, so any advice is appreciated.


Solution

  • I’m going to have a go at answering my own question, after doing some further research.

    My understanding is that any external service is basically a separate bounded context. In which case the Adapter and Pool are basically functioning as an ACL, by hiding the service behind a common interface. Is this assumption correct?

    Yes. It’s all about the language. Obviously the implementation of any external service uses a completely different language, and so must be a separate bounded context. We could choose a Conformist relationship with this BC, but that leaves us tightly integrated with the third party service. So we need something to “talk” to the other bounded context in our language. Call it an Adapter, a Gateway, whatever, it’s acting as an ACL.

    I need to raise events from my Adapter / Pool wrapping the external service. ACLs are often implemented as Domain Services, but the common advice is not to raise events from a Service. My reasoning is the Domain Service is an interface, and the Implementation (Infrastructure) goes ahead and raises events as needed. These are essentially Integration Events as opposed to typical Domain Events. I don’t own the BC, just the Adapter, so the typical guidance on Domain Events / Aggregates goes out the window? Just raise the event directly?

    I feel the literature out there on Domain Events can sometimes be a bit misleading. There is the core concept of a Domain Event: something that happened in the domain. But over time this concept has started to conflate with a number of different, but related things: read model projections in CQRS, eventual consistency, event sourcing and communication across bounded contexts. So we’ve had to define what a domain event means to us in our system.

    A domain event is a record of something changing in our domain, and so logically should only be raised by aggregates. You then choose whether to listen and handle those events. You also decide whether to handle within a transaction, or outside. We’re choosing to handle inside.

    After this, I believe we are now crossing over into Messaging. There is nothing to say you can’t publish a domain event as a message, but it has then become something a bit different, or perhaps better stated: now has added purpose. One type of Message is an Event, and it doesn’t have to be a domain event. And hence, I can raise from my Adapter if that makes sense. In this case it's representing an Event outside my BC. Although handling of that Event may result in domain events within our BC. It’s these Messages that allow us to implement eventual consistency, communicate across BCs etc. Call them Integration Events, Public Events, External Events, AsyncEvents as you will. Translate your Domain Events before publishing if you like, or publish them directly. At this point they are a Message, whether you're using a different class or not.

    In the instance of the Process Manager requesting a resource from the Pool. Does the Process Manager just call the Adapter directly? Or should this be a Command? Even if the Command is essentially just going to request a resource (ie. no state change to an entity).

    It depends. If the resource is not a core part of the business logic and language of the system, perhaps it can be called directly as a service from the PM. If it enables an integral concept within the domain, then the PM should probably raise a Command message. We can acknowledge that some state change is happening in our system. So some aggregate (even if just for tracking the state changes within the PM) will change state and result in a domain event. This will eventually be published as a Message. And the request will take place. Or perhaps we request the resource from the command using a domain service. Depends on what we want to achieve.

    Any best practises for raising events in this type of scenario? Likewise, if I decided to implement a supporting subdomain with basic Transaction Script. Assume you would just raise the events directly to the Bus as needed from the ACL. There is no Domain Model, and so everything is an Integration Event?

    We just acknowledge these other Events are just Messages. We handle them in our BC as we see fit. These aren’t Domain Events. Follow Messaging best practises.

    Purely for theoretical purposes, as I better try to understand the concepts. A Repository is essentially wrapping a third party persistence service. Is the line of reasoning that the persistence technology is a Bounded Context, and the Repository an ACL / Adapter technically correct?

    If we’re being pedantic, yes. The language used in the ORM etc means nothing to my BC. So the Repository translates between the two. Persistence isn’t a core concept of my domain, so we just call them directly from the Application layer.

    One of the keys for us was going back to Enterprise Integration Patterns by Gregor Hohpe and Bobby Woolf and Reactive Messaging Patterns with the Actor Model by Vaughn Vernon. It helped us distinguish our Messaging concepts from our Domain Events.