4

Taking into consideration the domain events pattern and this post , why do people recomend keeping one aggregate per transaction model ? There are good cases when one aggregate could change the state of another one . Even by removing an aggregate (or altering it's identity) will lead to altering the state of other aggregates that reference it. Some people say that keeping one transaction per aggregates help scalability (keeping one aggregate per server) . But doesn't this type of thinking break the fundamental characteristic about DDD : technology agnostic ?

So based on the statements above and on your experience, is it bad to design aggregates, domain events, that lead to changes in other aggregates and this will lead to having 2 or more aggregates per transaction (ex. : when a new order is placed with 100 items change the customer's state from normal to V.I.P. )?

Tudor
  • 1,133
  • 1
  • 12
  • 28

2 Answers2

6

There are several things at play here and even more trade-offs to be made.

  • First and foremost, you are right, you should think about the model first. Afterall, the interplay of language, model and domain is what we're doing this all for: coming up with carefully designed abstractions as a solution to a problem.
  • The tactical patterns - from the DDD book - are a means to an end. In that respect we shouldn't overemphasize them, eventhough they have served us well (and caused major headaches for others). They help us find "units of consistency" in the model, things that change together, a transactional boundary. And therein lies the problem, I'm afraid. When something happens and when the side effects of it happening should be visible are two different things. Yet all too often they are treated as one, and thus cause this uncomfortable feeling, to which we respond by trying to squeeze everything within the boundary, without questioning. Still, we're left with that uncomfortable feeling. There are a lot of things that logically can be treated as a "whole change", whereas physically there are multiple small changes. It takes skill and experience, or even blunt trying to know when that is the case. Not everything can be solved this way mind you.
  • To scale or not to scale, that is often the question. If you don't need to scale, keep things on one box, be content with a certain backup/restore strategy, you can bend the rules and affect multiple aggregates in one go. But you have to be aware you're doing just that and not take it as a given, because inevitably change is going to come and it might mess with this particular way of handling things. So, fair warning. More subtle is the question as to why you're changing multiple aggregates in one go. People often respond to that with the "your aggregate boundaries are wrong" answer. In reality it means you have more domain and model exploration to do, to uncover the true motivation for those synchronous, multi-aggregate changes. Often a UI or service is the one that has this "unreasonable" expectation. But there might be other reasons and all it might take is a different set of abstractions to solve the same problem. This is a pretty essential aspect of DDD.
  • The example you gave seems like something I could handle as two separate transactions: an order was placed, and as a reaction to that, because the order was placed with a 100 items, the customer was made a VIP. As MikeSW hinted at in his answer (I started writing mine after he posted his), the question is when, who, how, and why should this customer status change be observed. Basically it's the "next" behavior that dictates the consistency requirements of the previous behavior(s).
Tomasz Jaskuλa
  • 15,723
  • 5
  • 46
  • 73
Yves Reynhout
  • 2,982
  • 17
  • 23
2

An aggregate groups related business objects while an aggregate root (AR) is the 'representative' of that aggregate. Th AR itself is an entity modeling a (bigger, more complex) domain concept. In DDD a model is always relative to a context (the bounded context - BC) i.e that model is valid only in that BC.

This allows you to define a model representative of the specific business context and you don't need to shove everything in one model only. An Order is an AR in one context, while in another is just an id.

Since an AR pretty much encapsulates all the lower concepts and business rules, it acts as a whole i.e as a transaction/unit of work. A repository always works with AR because 1) a repo always deals with business objects and 2) the AR represents the business object for a given context.

When you have a use case involving 2 or more AR the business workflow and the correct modelling of that use case is paramount. In a lot of cases those AR can be modified independently (one doesn't care about other) or an AR changes as a result of other AR behaviour.

In your example, it's pretty trivial: when the customer places an order for 100 items, a domain event is generated and published. Then you have a handler which will check if the order complies with the customer promotions rules and if it does, a command is issued which will have the result of changing the client state to VIP.

Domain events are very powerful and allows you to implement transactions but in an eventual consistent environment. The old db transaction is an implementation detail and it's usually used when persisting one AR (remember AR are treated as a logical unit but persisting one may involve multiple tables hence db transaction).

Eventual consistency is a 'feature' of domain events which fits naturally a rich domain (and the real world actually). For some cases you might need instant consistency however those are particular cases and they are related to UI rather than how Domain works. Of course, it really depends from one domain to another. In your example, the customer won't mind it became a VIP 2 seconds or 2 minutes after the order was placed instead of the same milisecond.

MikeSW
  • 16,140
  • 3
  • 39
  • 53
  • Besides the scalability argument , are there any arguments to not use more than one aggregate change in one transaction ? Scalability can easly be made by making more instances of the application and let a load balancer do the work . Puting one aggregate per server/cluster of servers it seems an extreme case and too fine grained distribution of the load . – Tudor Apr 27 '14 at 18:39
  • 1
    I repeat that we're using aggregate **roots** not aggregate. You can change as many Ar as you like a many times as you like. To dedicate a server for 1 AR is a very special case, it's not the usual practice. With domain events and proper modeling and respecting separation of concerns you can build a very scalable app. But how exactly it's implemented, that's very app dependent. DDD is really high level, the decision to host only 1 AR per server is a low level technical decision, unrelated to DDD itself. – MikeSW Apr 27 '14 at 19:05
  • Eventual consitency leads to chaos in my opinion when the behaviour of a system is based on high consitency. What happens when some actions/commands sent to the domain requires accurate data, but this accurate data items are in that "inconsitent window". This will lead to chaos. Yes eventual consistency goes very well with the ideea of keeping 1 AR per transaction , but not with the actual bussiness model which may require accurate data all the time . – Tudor Apr 27 '14 at 19:40
  • Eventual consistency (EC) works quite fine with many systems, there's no chaos, only a delay. Also EC is unrelated to 1 AR per transaction (?!?), it's how things works in a (domain) event driven app. I don't know what you mean by a business model which requires accurate data ALL the time. I can tell you that a LOT of businesses don't have accurate data at a moment in time.Accuracy is quite relative here, by the time your browser renders my comment, the server data has changed and you see a stale page.You have to wait for the ajax refresh to see that maybe other user has left a comment. – MikeSW Apr 27 '14 at 20:05
  • Your example of what the viewer is seeing comes from replicas of the database, there EC is implemented from the write db (master) to the read db (slave), no problem here because the posts don't need to be views right at that specific milisecond when they were created. When it comes to the consistency of writes (the actual bussiness model state) eventual consistency is not good because there is an inconsitent window, a storage system will guarantee accurate/consistent data only if there are no eventual updates of the objects. These eventual updates represent that inconsistent window – Tudor Apr 27 '14 at 20:20
  • The write model allways needs to be accurate/consistent, the read model can be eventual consistent ... There are differences here. I was talking about the write model and not about the read model – Tudor Apr 27 '14 at 20:23
  • Even the write model's accuracy is relative. You deposit money into your account, it takes at least a few seconds since your money have left your hands until your account is credited. 1 second before that amazon decides to take money from your account (some european debit cards are seen as credit cards by payment processors but they still act as debit cards). Your account has a negative balance for 2 seconds. A few seconds earlier or later don't really matter even for banks. – MikeSW Apr 27 '14 at 20:34
  • If you're thinking about stock trading scenario, well there's something called event sourcing which worked great with it. It uses domain events and it's pretty eventual consistent. You need to have a very specific domain to have the 100% always need for accuracy and even then, chances are it doesn't really matter. However if you're sure DDD is not fit for you and you really want that db transaction then by all means use it. – MikeSW Apr 27 '14 at 20:38
  • Eventual consistency promote BASE principles while traditional transactions promote ACID principles . ACID principles guarantee full consistency. There are even XA, distributed transaction that promote ACID . Traditional transaction (ACID) won't make available any updates untill they are comited while writes using eventual consitency leave a gap, an inconsitent window, some data is available but the other data items that were triggered because of that available update might be unavailable (eventaul consistent) – Tudor Apr 27 '14 at 20:42
  • 1
    You are too hooked on low level details, a properly modeled app can happily use a 'transaction' with 3 AR one stored in a rdbms, other in a doc db and the third on a remote server. DDD is after all a mindset unrelated to ACID or a specific implementation. – MikeSW Apr 27 '14 at 20:47
  • Yes but the argument relating scalability of AR , by using one per server , breaks ACID rules if you change more than one. If you actually use ACID transactions with more than one AR, XA transactions would be required which leads to lower availability because of the network roundtrips. I was just asking of some opinions . Your's actually enlightened me a bit. I've just read some statements regarding this one AR per transaction and was wondering if it really is an almost best case scenario (i say almost because each design has its pros and cons). +1 for too hooked on low level details – Tudor Apr 27 '14 at 20:57
  • AR is a technical term for a domain concept. Nothing to do with ACID, the only 1 AR per transaction 'rule' has to do with the Repository. And the repo can use an ACID transaction to persist an AR and because the repository always deals with only 1 AR at the time you get 1 AR per transaction. Really, an implementation detail – MikeSW Apr 27 '14 at 21:02
  • I'll offer my take on the problem Tudor is expressing. If the Order AR fires a domain event before it is written to storage (most likely), then the event handler picks it up to evaluate the need for promoting a user to VIP status, that decision really depends on the state of data in storage. If the Order data has still not made it to storage, the event handler may not promote the user yet. If the handler evaluates after the write to storage, all is well. It's the potential for EC in that particular example that most likely concerns the OP. – ventaur Apr 29 '14 at 14:01
  • Granted, the event should contain the new Order object, but there is the chance that a couple of orders from the same customer came in close enough to skew the decision, when storage is queried for other orders, besides the one in the event. – ventaur Apr 29 '14 at 14:03
  • AFAIK an event is published only after the object is persisted and IMO it should always be like this (you want to make sure the changes were saved before announcing the world). Btw, the event doesn't contain the Order object itself but all the relevant Order changes (for creation it's the init values). – MikeSW Apr 29 '14 at 14:10