BC modelling and integration

104 views
Skip to first unread message

Tilman Linden

unread,
Jun 4, 2016, 4:25:23 PM6/4/16
to DDD/CQRS
Hi there,

as this is my first post on this list, let me quickly introduce myself. My name's Tilman, I am working for a German eCommerce startup. My team and me are creating a CQRS/ES based eCommerce application based on Scala. Our general approach is I think quite in line to what most people on here seem to do - all write integration is purely based on events being dispatched between aggregates and contexts. In addition to that we allow cross-context calls for reading data from other contexts. We implement Ports and Adapters in order to have anti corruption layers in between.

Let me sketch out an example from the eCommerce domain. Consider a context "Purchasing" that is all about doing purchase orders from suppliers, exporting expected stock-receipts to logistic providers and importing actual stock-receipts back. Purchase orders generally contain a good amount of data that originates from other contexts, e. g. contract terms from the "Buying" context, product details from the "Catalog", shipping time frames from the "Shipping" context and so on.

With the above mentioned architectural approach, I see different possibilities for getting the state from other contexts into the "Purchasing" context. Let's think about the use case "create purchase order":

a) We could constantly replicate all state needed for a new purchase order to an aggregate in "Purchasing" by means of dispatching events to that aggregate, e. g. to some "PurchaseOrderTerms". This aggregate would work as a factory for a new "PurchaseOrder" aggregate, passing all data that it has collected from other contexts earlier to the new order during it's creation.
b) We could get all data needed from other contexts via cross-context-calls in the moment the "CreatePurchaseOrder" command arrives at the "Purchasing" context.
c) We could pass all data needed for the purchase order within the actual "CreatePurchaseOrder" command, i. e. the whole content of the purchase order would be submitted by the user already (and of course the form would have been prefilled from data in other contexts, so we kind of move the cross-context-dependency to the application layer).

We were mostly focussing onto a) and/or b) until I recently re-read some of Nygard's articles about maneuverability and semantic coupling, e. g. http://www.michaelnygard.com/blog/2015/04/manueverability/ and http://www.michaelnygard.com/blog/2015/04/the-perils-of-semantic-coupling/

I found that a) and b) in the end create a lot of dependencies between contexts, although we have ACLs in place. With c) it seemed much easier to think about a completely self-contained "Purchasing" context that does not need to have any knowledge about other contexts' existence, and has a single well-defined entry point.
What we would sacrifice though, is the ability to validate things like "does item X at all belong to contract Y?" (because we do not have any contract information anymore in the "Purchasing" context, or "is the current name of SKU X really Y?" (because we do not query the catalog anymore).
We would simply accept the order in the "CreatePurchaseOrder" command to be in line with contract terms and so on and would only execute "local" validations (e. g. does a drop-shipping purchase order contain a dispatch date for every item and so on).
On the other hand with c) we can be pretty sure that the order in the end is exactly what the user submitted. If we rely on asynchronous state as in a) and b), it could always happen that the user submits an order but some property (e. g. a product name) is affected by concurrent updates in the moment of command submission.

I find this a very interesting trade-off and would be interested in other people's opinions or experiences.

@yreynhout

unread,
Jun 8, 2016, 11:59:14 AM6/8/16
to DDD/CQRS
- This is not a validation issue, it's a trust issue. I'm assuming the bounded contexts trust each other but not the potentially malicious user of the system. There's a lot of prior art (hint: encryption) that can help in proving that information actually came from a particular source and wasn't tampered with in transit. As such, not a trade-off you need to make.
- Bounded Contexts are logical units - from a deployment point of view you could replicate data very close to where it is needed, even if it belongs to another BC. In essence, still cross-context-calls, but hopefully without a slow network in between :-)
- WYSIWYS - you heard it here first - what you see is what you send. Capture the version of what an end user is looking at in your command (e.g. product 12345 at version 5). Nothing a concurrent update can do about that ;-)

Tilman Linden

unread,
Jun 9, 2016, 12:18:18 PM6/9/16
to DDD/CQRS
Some interesting points, thanks!

- Validation vs Trust - You're absolutely right about this, I've never thought about it this way.
- CCCs/Whatever BC dependency - I wasn't too much concerned about network latency here, more about the logical dependency. E. g. when "Purchasing" receives data of the purchased products (e. g. name, color, etc.) from the "Catalog", I implicitly say that "Purchasing" can only work on stuff that's in our catalog. If I want to use "Purchasing" logic for other things (that can be purchased), I'd have to add an integration to other BCs.
Reply all
Reply to author
Forward
0 new messages