4

Question:

  • If I have to fetch an entity from another Bounded Context in order to map it to something in this Bounded Context, how would I go about doing that?
  • Do I call the foreign Bounded Context's Application Layer? Perhaps I need a new "Query" Application Layer for such queries?
  • Do I call foreign Bounded Context's repositories?
  • Do I call foreign Bounded Context's Domain model directly?

Domain model (please note two separate Bounded Contexts):

  • CustomerContext.Customer - this is the complete Customer model.
  • ConsentContext.Person - a scaled-down, heavily transformed version of multiple instances of CustomerContext.Customer. Rules guiding this transformation reside in ConsentContext.Person. Person never references ˙Customer` directly, no dependency here.
  • Please note: these two entities most definitely belong to separate Bounded Contexts. Customer is what the business works with on a daily basis. Consent Person is heavily transformed due to legal and business requirements and has a different structure altogether. Customer is merely a data source, so Person can create itself using internal business logic.

Application Layer:

  • ConsentContext.ApplicationService - implements use cases. As part of those use cases it fetches ConsentContext.Person through some PersonRepository.

Repos:

  • ConsentContext.PersonRepository has to get in touch with the CustomerContext and retrieve the CustomerContext.Customer and map to a new Person. This is where I am coming up short - what do I call from here? CustomerContext's Application Layer? CustomerContext's repositories? CustomerContext's Domain model directly?

Other:

  • Both Bounded Contexts run in the same process, I am not using a REST API.
  • I am targeting Customer/Supplier relationship.
4
  • I'm not entirely sure I understand why these two entities are both required. How is Person created? Generally, repositories are used to persist/load your model, not create it. Is PersonRepository.find( customerId ) not possible? These two entities seem like they may share an ID. That is, traditionally, how aggregates/entities are referenced between contexts. Commented Jul 26, 2018 at 20:38
  • This seems like a prime example of drawing boundaries around data points instead of behavior. Your system should be modeled according to how it behaves, not arbitrarily according to organizational boundaries. If the behavior is such that two entities from different contexts are required for a single transaction, it may be an indication that these two contexts (or portions of each) need to be combined/refactored. Commented Jul 26, 2018 at 20:39
  • @king-side-slide: it is true we do not store Person anywhere. It is reconstituted every time anew, based on the customerId and Customer. We do store Person in a dedicated database, however.
    – robotron
    Commented Jul 26, 2018 at 20:42
  • @king-side-slide: Person requires one or two separate Customer's to be created. There is a complex relationship between customers (typical example: two customers belong to the same account: one might be an account owner, but the other one might be relevant for Consent gathering, due to various legal reasons). So it is not that simple to map Customer to Person - we mainly take both customers name, id, VAT, whether they are business or residential and then use these to instatiate a Person (based on person internal logic, in ctor). Instantiation is done in PersonRepository.
    – robotron
    Commented Jul 26, 2018 at 20:46

2 Answers 2

6

I’m going to sidestep the discussion of whether there really are two bounded contexts, and address the question of how they can share data. Before doing that, however, I have to point out that you are doing yourself a disservice by having 2 bounded contexts in the same process. From Microsoft’s definition

Bounded contexts are autonomous components, with their own domain models and their own ubiquitous language. They should not have any dependencies on each other at run time and should be capable of running in isolation. However they are a part of the same overall system and do need to exchange data with one another.

A big advantage of bounded contexts is their independence from one another. If one goes down, the other should survive and be able to keep functioning. This also helps in scalability by allowing deployment on different servers if the app gets more popular than expected.

Note that 2 process spaces doesn’t have to mean 2 servers. They can both share the same hardware but should have as little coupling between them as possible, meaning no coupling in code.

Along the coupling idea, we have this from Eric Evans

Code reuse between BOUNDED CONTEXTS is a hazard to be avoided.

All of the options provided above violate this guidance.

So your question has to do with the final sentence of the Microsoft definition of a Bounded Context. These are your options in order of preference

Each context can cache the information in needs from the other contexts. This is done via an event queue or message queue that all contexts share, and as important aggregates change they inform other interested contexts. Julie Lerman has a good discussion of it here. If you want to stick with a single piece of hardware, the message queue can also run on that hardware, though that would introduce some scalability and durability concerns.

Second option is to add an API. I know you say you don’t have one, but APIs can be deployed as part of the context so they are little extra work. They can also be locked down to only accept requests from localhost using CORS rules.

Third option is a shared database, but now we’re coupling the contexts so this is a dangerous road. If you MUST do this, at least make the tables in separate schemas so the developer is aware they are breaking the wall between the contexts.

I would avoid all three of the options you have listed. It’s not hard to imagine requirements changing in one context that conflict with the rules in another. After all, that’s why you separated them into separate contexts to begin with.

9
  • Thank you for the detailed write up. Interestingly, so none of the options actually allow for fetching data directly via the other context's repositories as that would introduce coupling. I watched several videos on Pluralsight and the topic of communicating with other Bounded Contexts was never present, so I just improvised. I will give my comment on each of the options in comments below.
    – robotron
    Commented Jul 28, 2018 at 10:23
  • 1
    API: Let's forget the two Bounded Contexts I mention above for a moment and imagine I have some other two BCs. If I were to place both BCs behind API, each BC would still have an Application Layer sitting between the API and Domain Layer (as per Onion Arch.), through which the API call would have to go through. Application Layer implements various use cases. If I were to fetch some data via the API, perhaps I do not wish to execute complete use cases, perhaps I just want a Domain Entity/DTO, take the data and map/transform it to an entity in the other BC. How would I go about doing that?
    – robotron
    Commented Jul 28, 2018 at 10:30
  • 1
    Migrating to a BC is a much tougher task than greenfield. Shared database is a compromise that seems to make things easiest while fighting the battle of teasing apart the contexts. With 700K+ users, I would not be surprised if you start having performance issues soon, though, making the breakup of BCs into separate address spaces all the more urgent.
    – Brad Irby
    Commented Jul 28, 2018 at 10:31
  • 1
    I completely agree the DB integration is a code smell, but when migrating sometimes you have to hold your nose. greenfield is about doing it right so you don't have to do it again. Migration is about moving the needle. It's rarely possible to get all the way to the target architecture in one step
    – Brad Irby
    Commented Jul 28, 2018 at 10:36
  • 1
    If you are just querying via the API there should be no business rules to execute. At most there will just be some table joins, unless you're doing CQRS (which I like) and the read model is already updated. You can wrap that query in an Interactor if you think necessary, but if you're just querying to fill a View Model, maybe you can use a shortcut.
    – Brad Irby
    Commented Jul 28, 2018 at 10:39
1

If Customer is a datasource for Person, then its fine to have a PersonRepository reference Customer and a CustomerRepository.

That doesn't break the bounded context because PersonRepository is a service rather than an Entity/Domain Object.

PersonRepository should contain the transformation rules rather than Person

2
  • 'PersonRepository should contain the transformation rules rather than Person' - there are two transformations going on. 1) mapping Customer attributes to what the Person requires and 2) Legal and business logic pertaining to the Consent Bounded Context. But I do understand what you are aiming at: Anti-Corruption Layer and Context Mapping.
    – robotron
    Commented Jul 26, 2018 at 9:12
  • I have listed three different ways for fetching entities from CustomerContext: application layer, repo, domain model. Would you say each of these is a valid way of fetching data, depending on the circumstances? Or are some definitely off-limits, like going for the domain model directly?
    – robotron
    Commented Jul 26, 2018 at 9:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.