Confused about communicating between bounded contexts.

270 views
Skip to first unread message

Jordan Neuhart

unread,
Jul 24, 2015, 10:37:37 AM7/24/15
to DDD/CQRS
I've looked through as many posts as I could find and read through Implementing Domain Driven Design and the blue book, but I'm still confused about the concept of bounded contexts in general and specifically how you communicate changes between them. As an example, I'm working on implementing a LIMS system for a laboratory that analyzes agricultural samples (soil, feed, water, etc.). The high level view is something like this:

1. Samples are received from the customers with an order sheet that has a list of their samples and the analyte test package they want.
2. The samples are "logged in", which means that ids are assigned the samples and the test package on the order sheet is translated into the individual analytes in the package and all the information is recorded for internal use and later reporting.
3. The samples are batched and approved for testing, which means that the lab techs are given the go-ahead to use their instruments to determine the concentrations of the various analytes of interest (nitrogen, phosphorous, etc.).
4. The lab techs are given a task list with the samples and analytes they need to measure for the day and they get to work. There is also some additional quality control performed in the lab on the individual instruments at the beginning of each day.
5. When the techs are done, they upload their measurements (or in the paper world write them down and give the to an analyst) and analysis is performed to transform the direct measurements into other higher level analytes of interest (bulk density, cation/anion balance, etc.)
6. Approved reviewers approve the results of the analysis, an invoice is generated, and the approved results and invoice are sent to the customer.

The bounded contexts that the team has identified for a system supporting this are:

-Identity & Access (for user management)
-Account Management (for supporting Billing and Sample Receiving and Reporting)
-Sample Receiving (Everything in 1 and 2 and part of 3)
-Billing (Invoicing the customer and integrating with external accounting system)
- Lab (Everything related to the lab work portion of the process, i.e. tech result task lists, integration with lab instruments, and quality control in the lab)
-Analysis (Defining of analytes and analyte packages, equations for performing analysis, the actual calculation of final results before review, as well as approval of samples by reviewers.)
-Reporting (Reporting results to customers after review)

First of all, do these seem like reasonable bounded contexts, or are they really too small in their own right? We are a small team, it's a small business, and we are in charge of the whole project. Does it really make sense to have so many separate contexts in this case?

Secondly, do bounded contexts require some sort of separation as far as deployment is concerned. When running on a single host, do they require separate processes, separate databases? Since we are small, we'd like to get away with just having a single EventStore and as few processes as possible.

Finally, how do you communicate changes between bounded contexts in a CQRS/Event Sourced architecture? Say I wanted to communicate a customer address change from Account Management to Billing. Is it alright to have an event handler living in the Billing context that subscribes to a CustomerAddressChanged event from the account management context and that issues a command to update the local account model in the Billing context. Should this handler actually live outside of both the Account Management context and the Billing context in it's OWN independent context?

Thanks to anyone who can shed some light on this for me.

Jordan



Johanna Belanger

unread,
Jul 24, 2015, 3:40:30 PM7/24/15
to DDD/CQRS, jjne...@gmail.com
Bounded Contexts are a logical separation, and they are about language. A Sample in the Lab context will have different behavior than a Sample in the Billing context. Separate processes and databases are not required. You might want separate namespaces, or you might use names like LabSample and BillingSample.

Events are a good way to communicate between bounded contexts. Sometimes an event might require some translation to make sense in the receiving context. Be sure to do this translation at the boundary (for example, in the event handler) so that it doesn't pollute or confuse the language in the receiving context.

That's my understanding, anyway. =)

jarchin

unread,
Jul 26, 2015, 4:32:04 PM7/26/15
to DDD/CQRS, jjne...@gmail.com
Hi Jordan, and thanks for a very explicit description of the domain.

In my project the different bounded contexts are deployed to the same host. It's a small project. It is to some extent built so that each BC could be separately deployed. There was the overhead with the distributed architecture that kept them all in one deployment.
Now, as they are hosted together, I have decided to go down a slightly slippery slope, and just skip the command part of the communication between process managers and AR:s. The command-part is a redundant step when we're not distributed. So the process managers acts as stateful command handlers, only that they are triggered by events rather than commands. The commands for the exact same operations still exist, so if we would like to call that AR method manually (via the command handler), we fire off the command with a button click in UI.
The write side has its top level dll with the process managers, which references all the BC-dlls. A single subscriber singleton in the processmanager dll subscribes to all events from event store, and have all router singletons referenced. These routers have all process manager types registered by the event type that they listen to. So event comes in and gets asynchronously routed out to the correct process manager(s). Process manager instances are event sourced and run synchronously.

So, the obvious problem with skipping the command step is that the process managers will have to be rewritten if any of the BCs are to be moved to separate deployments. It was a sensible decision in this case though.

I have struggled some with BC sizing too. Unfortunately I don't have any good advice. I can only say that the ones you suggest seem reasonably granulated.

Jordan Neuhart

unread,
Jul 27, 2015, 9:31:45 AM7/27/15
to DDD/CQRS, jarch...@gmail.com
I'm curious about your process managers. What are you using for your state events in your process manager? Are you duplicating and saving the original event as a state event in the process manager, or do you have another event like ProcessManagerTransitionedToState or something like that?

@yreynhout

unread,
Jul 27, 2015, 1:53:30 PM7/27/15
to DDD/CQRS, jjne...@gmail.com
1) It's fine for small teams to work on multiple contexts. In fact you'll be less inclined to be stepping on each other's code compared to the monolith. Sometimes boundaries need to be made really clear in the code artifacts for a team to understand the separation between contexts. That, I'm afraid, is for you to assess, how capable your team is of making such distinction. Having multiple contexts is good to narrow conversations to the essentials, for setting a "context" in which a conversation is taking place, bringing meaning to the words without ambiguity, knowing who to talk to, etc ... lots of minute benefits ensue that you wouldn't otherwise experience. At least, such is my experience.

2) Sometimes namespaces and modules will do, other times a separate database schema or folder in your repository will do. You could even put each context into its own source control repository (assuming something like git/hg). Whatever works best for your team. Approach it in an organic fashion, learn what works best as you go along. This, however, is orthogonal to how you deploy things. For all I care all BC artifacts end up in one big executable, hosted together, or not ... it's a different angle of looking at things.

3) Whether to expose your events to other contexts is more a debate of messaging, contracts, and versioning and how sane it would be do so. The discussion in the billing BC should be geared towards "if a customer changes his address, how soon should that be reflected on the account? What does that even mean? Does it mean his billing address changed? Or that we should show it as a new potential billing address?". I'm just making this up, but there's quite a bit of conversation that needs to happen to really know what form of integration suits a BC best for a particular dataset. For sure, you can use events as your integration model, but be sure that is really what you need/want. Closed systems where BCs are set up just to make the various problems decomposed may need to worry less ... magic 8-ball mode.

@yreynhout

unread,
Jul 27, 2015, 2:25:39 PM7/27/15
to DDD/CQRS, jjne...@gmail.com
Jordan Neuhart wrote
"Thanks. I'll take what you wrote into consideration. One question though. On point 3, what would be the alternative to exposing your events to other contexts? How would you inform other interested contexts of a change?"

- A data feed comes to mind (much like an atom feed). But also the question as to which context owns the data (if applicable). The reason why data is being copied is really important and should be questioned. Copying often means maintaining which might not be what you want. E.g. if the customer's address is only needed when sending out the bill, it might be better to compose there and then. If the address is important for auditing purposes, things might be different, but then a copy is probably more appropriate. Integration might be done at the UI and not as some replication of data from one context to another. Requirements dictate a lot I'm afraid. You can't expect a clear answer from me with this little information.


On Friday, July 24, 2015 at 4:37:37 PM UTC+2, Jordan Neuhart wrote:

jarchin

unread,
Jul 28, 2015, 4:34:50 PM7/28/15
to DDD/CQRS
Yes, that's exactly what I have.
In more detail:
Lets say I have one event that triggers calling a method on n different aggregates.
I then have a distinct 'process-event' for every one of those n method calls, that is raised in a method of the PM when that AR method is successfully called and the AR's events written to eventstore. That PM event updates the PM state (method on AR n successfully called). While n-x, where x<n, of these method calls have been called and the process manager method has run to completion, the event that triggered the process manager is not completed on it's bus, thus it is received again until either dead lettered or all calls have succeeded. Every time the event arrives at the PM, it only tries to call those AR's not yet done ( if(_callingMethodOnFooARNotDone) { callMethod }. Depending on the AR/business logic, some errors will also let the PM consider the job done, and allow the event to be completed on the bus.

Jordan Neuhart

unread,
Jul 28, 2015, 5:06:29 PM7/28/15
to ddd...@googlegroups.com
Ok, I think that makes sense to me. The only case of failure I could see would be if the process manager is blocked from persisting it's state (uh oh, somebody unplugged me) after it gets an acknowledgment that the operation on the AR completed successfully and that AR had already saved it's state. Then, the next time the PM came back up it would try to call the method on the AR again. Are your ARs somehow idempotent or do you just not worry about this because it would be so highly unlikely?

--
You received this message because you are subscribed to a topic in the Google Groups "DDD/CQRS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/dddcqrs/HeTi8ABAHeQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to dddcqrs+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Reply all
Reply to author
Forward
0 new messages