Background
We were designing a micro-service to manage bookings in our system. So we decided to solve this using state-machine abstraction, i.e Bookings can be in one of multiple states, and can be transitioned to another through some actions/events.
We had a very healthy discussions between me and my colleague and I had some different opinion about how the state machine should be designed.
I took an example of REDUX, where we have different ACTIONS (EVENTS + PAYLOAD) and based on ACTION.TYPE we transition our system to another STATE.
This was fairly simple but my friend introduced confusing factor like ACTIONS and EVENTS: two complete different things (which in my opinion should be same in any state-machine)
And he is arguing that EVENTS cause some ACTIONS to perform on a state-machine to transition to some another STATE. And his point is EVENTS & ACTIONS are two complete different aspects and should not be same. They have different points and implications.
For e.g Certain EVENT may be CREATE_REQUEST , but actually looking for availability and making a booking is a ACTION derived by CREATE_REQUEST event.
But I completely disagree and I am strongly saying an ACTION should be something like [CREATE_REQUEST, {DATES, ...OTHER_INFO}] and the machine should perform accordingly and transitioned to some other STATE .
so what should be the correct approach.
├── src
| ├── ACTIONS
| ├────── CREATE_REQUEST // return {TYPE: CREATE_REQUEST, {data}}
| ├────── DECLINE_REQUEST //return {TYPE: DECLINE_REQUEST, {id}}
| ├── STATES
| ├────── DRAFT
| ├────── PENDING
| ├────── REQUESTED
| ├────── COMPLETE
I am waiting for some expert suggestions and advice so that we can have a clear vision on what to do.
The action represents what the user interacts with. The event is how your application deals with said interactions. It is wise to implement an arbitration layer in most cases, depending on the size of the project. Once you are convinced of the importance of the architecture of the application you should implement events handling the user actions. You simply don't want to put all your business logic into the actions. Doing so is a surefire way to making the code unmaintainable.
Related
I am applying Domain driven design in my project and I am running into a scenario where a user action translates into a command in one of my bounded contexts and thus produces an event. However I see that none of my other bounded contexts would care about consuming this event. Essentially all this command is doing is saving/ updating state in my bounded context.
My questions are :
does a command have to produce an event ?
If so, does it matter that nobody is listening ?
does a command have to produce an event ?
Absolutely not.
If you were using event sourcing as your persistence strategy, then all of your changes of state would be "events". But there's no particular reason that you must expose the event elsewhere.
Hyrum's Law is one reason that you might prefer not to broadcast an event
With a sufficient number of users of an API,
it does not matter what you promise in the contract:
all observable behaviors of your system
will be depended on by somebody.
Don't guess what information should be included in the event until you have enough data at hand to make a good guess.
does it matter that nobody is listening ?
In an ideal world, not really -- in practice, costs may well figure into the decision.
It is not a necessity for a command to produce an event, though it is preferable.
One of the most significant benefits of events is the ability to generate the state of the system as on a particular date/time from scratch. If you were to religiously bubble-up events for all changes in the system, you will be able to apply events sequentially and bring the system to the state of your requirement.
IMHO, it does not matter that nobody is listening at that point in time. I am assuming that you are persisting your event into an event sink/log as part of your application. Doing so has two benefits:
a. If you were to discover a future requirement which would benefit from an event log, you are already a step ahead.
b. Even if nobody ever consumes the event, as mentioned in the first response, being able to generate a state of the system as on a particular date/time is a cool ability to have.
The pattern of persisting events and deriving a system state has its roots in Event Sourcing. You would want to take a deeper look at it to see its benefits.
does a command have to produce an event ?
No, its not a requirement, a command can just modify a state of an entity without dispatching an event. As said in others responses you should require every state change to produce a domain event if you use Event Sourcing.
If so, does it matter that nobody is listening ?
Yes and No.
Yes because there is no good or bad model, only useful model, if no one is listening then your modeling is not really useful to anyone.
No because domain event are called invariant, they reflect business event and change only when the business requirement change, and so they are part of your API (see the concept of published language).
I am just starting out with ES/DDD and I have a question how one is supposed to do reporting in this architecture. Lets take a typical example, where you have a Customer Aggregate, Order Aggregate, and Product Aggregate all independent.
Now if i want to run a query across all 3 aggregates and/or services, but that data is each in a separate DB, maybe one is SQL, one is a MongoDB, and one something else. How is one supposed to design or be able to run a query that would require a join across these aggregates ?
You should design the Reporting as a simple read-model/projection, possible in its own bounded context (BC), that just listen to the relevant events from the other bounded contexts (Customer BC, Ordering BC and Inventory BC) and builds the needed reports with full data denormalization (i.e. at query time you won't need to query the original sources).
Because of events you won't need any joins as you could maintain a private local state attached to the Reporting read-model in which you can store temporary external models and query those temporary read-models as needed thus avoiding external additional queries to the other BCs.
An anti-corruption layer would not be necessary in this case as there would be no write-model involved in the Reporting BC.
Things are really as simple as that because you already have an event-driven architecture (you use Event sourcing).
UPDATE:
This particular solution is very handy in creating new reports that you haven't thought ahead of time. Every time you thing about a new report you just create a new Read-model (as in you write its source code) then you replay all the relevant events on it. Read-models are side-effect free, you can replay all the events (from the beggining of time) any time and as many time you want.
Read-model rebuilding is done in two situations:
you create a new Read-model
you modify an existing one by listening to a new event or the algorithm differs too much from the initial version
You can read more here:
DDD/CQRS specialized forum - Grey Young is there!
Event sourcing applied – the read model
Writing an Event-Sourced CQRS Read Model
A post in first group describing Read Model rebuilding
Or you can search about this using this text: event sourcing projection rebuilding
Domain-Driven Design is more concerned with the command side of things. You should not attempt to query your domain as that leads to pain and suffering.
Each bounded context may have its own data store and that data store may be a different technology as you have stated.
For reporting you would use a reporting store. How you get data into that store would either require each bounded context to publish events that the reporting BC would pick up and use to update the reporting store or you could make use of event sourcing where the reporting store would project the events into the relevant reporting structures.
There are known practices to solve this.
One might be having a reporting context, which, as Eben has pointed out, will listen to domain events from other contexts and update its store. This of course will lead to issues, since this reporting context will be coupled to all services it reports from. Some might say this is a necessary evil but this is not always the case.
Another technique is to aggregate on-demand. This is not very complex and can be done on different layers/levels. Consider aggregation on the web API level or even on the front-end level, if your reporting is on the screen (not sent by mail as PDF, for example).
This is well known as UI composition and Udi Dahan has wrote an article about this, which is worth reading: UI Composition Techniques for Correct Service Boundires. Also, Mauro Servienti has wrote a blog post about this recently: The secret of better UI composition.
Mauro mentions two types of composition, which I mentioned above. The API/server-side composition is called ViewModel Composition in his post, and front-end (JavaScript) composition is mentioned in the Client side composition process section. Server-side composition is illustrated by this picture:
DDD strategic modeling tools says:
Design two different models 1. Write Models (Handles Command Side) 2.Read Models (POCOs/POJOs) whatever u call them.
My domain is about Program Management. I have a Program (Aggregate Root) that must have a Customer (Aggregate Root). So I require a CustomerID when creating a new Program as I have read aggregates should only hold reference to other aggregates by reference.
Here are my business rules:
Customers can become active and inactive over time.
If a Customer is inactivated for some reason, all programs associated with that Customer should also be inactivated.
A Program cannot be activated if its Customer is inactive.
Rules #1 & #2 I have implemented. It's #3 that is stumping me.
I can think of 3 solutions:
Program holds reference to the Customer aggregate.
Introduce a domain service that checks if the Customer is active and pass it to Program.Activate(CustomerActiveCheckService service).
Have the application service look up the Customer and pass it to Program.Activate(Customer customer).
Which is the best solution?
Update
I see both points of view made by #ConstaninGALBENU and #plalx, and I want to suggest a compromise. Can I created a CustomerStatusChecker service? The method would have the following signature: CustomerStatus CheckStatus(CustomerID id); I could then pass Programthe service like so: `Program.Activate(CustomerStatusChecker service);
Are there any problems with this design?
Which is the best solution?
There isn't a best solution; there are trade offs.
But one possible solution that is consistent with requirements #2 and #3 is that your existing model is wrong -- that Program entities are not isolated aggregates, but are part of the Customer entity, and therefore should be controlled by the same aggregate root.
Hints that this might be the case: that the life cycle of a Program fits within the life cycle of a Customer; that Programs don't normally migrate from one Customer to another, that there are limits to the count of active programs per customer.
Another possibility is that the requirements are "wrong". One way of exploring this is to review whether active/inactive is a decision made by the model, or if it is a decision made somewhere else and reported to the model. Another is to examine the cost to the business if this "rule" is violated.
If the model doesn't find out about the customer right away, or it is an inexpensive problem, then you probably have some room to detect the conflict and report it to a human, rather than trying to have the model do all of the work (See: Greg Young, Stop Over Engineering).
In these cases, having the main code path take a good guess, and implementing an alternative path that operators can use fix the mistakes is fine.
In choosing between solution #2 and #3 (I don't like #1 at all), I encourage keeping I/O actions out of the model. So unless you already have the latest version of the Customer in memory, I'm not fond of the domain service as a choice. Passing in a copy of the customer state to the domain model keeps the I/O concerns in the application component, where they belong (see Boundaries, by Gary Bernhardt, for more on this idea).
Solution 1: it breaks the rule about not holding references to other aggregate instances. That rule ensures that only one Aggregate is modified in a transaction. If you need to modify multiple aggregates in a single transaction then your design is definitely wrong.
Solution 2: I really don't like injecting services inside aggregates. My aggregates are pure functions with no touching of the outside world (I/O, repositories or the like).
Solution 3: is somehow equivalent to 1, even it is a temporary reference (Program could call command methods on Customer thus modifying Customer in the same transaction boundary as Program) .
My solution: make that check inside the Application service, before that call to Program.activate () or pass a customerStatus to Program.activate () and let Program aggregate decide if it throws an exception or emit events.
Update:
The idea is that you should pass only read-only/imutable data to Program AR to ensure that it does not modify other ARs in its transactional boundary. Also, we should not make Program dependent on what it does not need, like the entire Customer AR.
Also, if the architecture is event-driven then by listening to the right events emited by Customer you could keep the Program AR in sync: you make it "non activable" if not already activated or you deactivate it if it is activated already, using by example a Saga.
I am new to DDD. Now I was looking at the domain event. I am not sure if I understand this domain event correctly, but I am just thinking what will happen if domain event published failed?
I have a case here. When a buyer order something from my website, firstly we will create a object, Order with line of items. The domain event, OrderWasMade, will be published to deduct the stock in Inventory. So here is the case, what if when the event was handled, the item quantity will be deducted, but what if when the system try to deduct the stock, it found out that there is no stock remaining for the item (amount = 0). So, the item amount can't be deducted but the order had already being committed.
Will this kind of scenario happen?
Sorry to have squeeze in 2 other questions here.
It seems like each event will be in its own transaction scope, which means the system requires to open multiple connection to database at once. So if I am using IIS Server, I must enable DTC, am I correct?
Is there any relationship between domain-events and domain-services?
A domain event never fails because it's a notification of things that happened (note the past tense). But the operation which will generate that event might fail and the event won't be generated.
The scenario you told us shows that you're not really doing DDD, you're doing CRUD using DDD words. Yes, I know you're new to it, don't worry, everybody misunderstood DDD until they got it (but it might take some time and plenty of practice).
DDD is about identifying the domain model abstraction, which is not code. Code is when you're implementing that abstraction. It's very obvious you haven't done the proper modelling, because the domain expert should tell you what happens if products are out of stock.
Next, there's no db/acid transactions at this level. Those are an implementation detail. The way DDD works is identifying where the business needs things to be consistent together and that's called an aggregate.
The order was submitted and this where that use case stops. When you publish the OrderWasMadeevent, another use case (deducting the inventory or whatever) is triggered. This is a different business scenario related but not part of "submit order". If there isn't enough stock then another event is published NotEnoughInventory and another use case will be triggered. We follow the business here and we identify each step that the business does in order to fulfill the order.
The art of DDD consists in understanding and identifying granular business functionality, the involved aggregates, business behaviour which makes decisions etc and this has nothing to do the database or transactions.
In DDD the aggregate is the only place where a unit of work needs to be used.
To answer your questions:
It seems like each event will be in its own transaction scope, which means the system requires to open multiple connection to database at once. So if I am using IIS Server, I must enable DTC, am I correct?
No, transactions,events and distributed transactions are different things. IIS is a web server, I think you want to say SqlServer. You're always opening multiple connections to the db in a web app, DTC has nothing to do with it. Actually, the question tells me that you need to read a lot more about DDD and not just Evans' book. To be honest, from a DDD pov it doesn't make much sense what you're asking.. You know one of principles of DD: the db (as in persistence details) doesn't exist.
Is there any relationship between domain-events and domain-services
They're both part of the domain but they have different roles:
Domain events tell the world that something changed in the domain
Domain services encapsulate domain behaviour which doesn't have its own persisted state (like Calculate Tax)
Usually an application service (which acts as a host for a business use case) will use a domain service to verify constraints or to gather data required to change an aggregate which in turn will generate one or more events. Aggregates are the ones persisted and always, an aggregate is persisted in an atomic manner i.e db transaction / unit of work.
what will happen if domain event published failed?
MikeSW already described this - publishing the event (which is to say, making it part of the history) is a separate concern from consuming the event.
what if when the system try to deduct the stock, it found out that there is no stock remaining for the item (amount = 0). So, the item amount can't be deducted but the order had already being committed.
Will this kind of scenario happen?
So the DDD answer is: ask your domain experts!
If you sit down with your domain experts, and explore the ubiquitous language, you are likely to discover that this is a well understood exception to the happy path for ordering, with an understood mitigation ("we mark the status of the order as pending, and we check to see if we've already ordered more inventory from the supplier..."). This is basically a requirements discovery exercise.
And when you understand these requirements, you go do it.
Go do it typically means a "saga" (a somewhat misleading and overloaded use of the term); a business process/workflow/state machine implementation that keeps track of what is going on.
Using your example: OrderWasMade triggers an OrderFulfillment process, which tracks the "state" of the order. There might be an "AwaitingInventory" state where OrderFulfillment parks until the next delivery from the supplier, for example.
Recommended reading:
http://udidahan.com/2010/08/31/race-conditions-dont-exist/
http://udidahan.com/2009/04/20/saga-persistence-and-event-driven-architectures/
http://joshkodroff.com/blog/2015/08/21/an-elegant-abandoned-cart-email-using-nservicebus/
If you need the stock to be immediately consistent at all times, a common way of handling this in event sourced systems (can also in non-event based systems, this is orthogonal really) is to rely on optimistic locking at the event store level.
Events basically have a revision number that they expect the stream of events to be at to take effect. Once the event hits the persistent store, its revision number is checked against the real stream number and if they don't match, a conflict exception is raised and the transaction is aborted.
Now as #MikeSW pointed out, depending on your business requirements, stock checking can be an out-of-band process that handles the problem in an eventually consistent way. Eventually can range from milliseconds if another part of the process takes over immediately, to hours if an email is sent with human action needing to be taken.
In other words, if your domain requires it, you can choose to trade this sequence of events
(OrderAbortedOutOfStock)
for
(OrderMade, <-- Some amount of time --> OrderAbortedOutOfStock)
which amounts to the same aggregate state in the end
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I am working on an open-source project which shows how to develop software iteratively using test-driven development and some ideas from domain-driven design. I know what I would do, however my formal skills (especially regarding UML) are a little bit rusty.
Below I am going to post my first steps with the diagrams and the explanations I would give to my readers.
I have the following questions:
The last step ends with the question But what will stich together these components?. How to show the reader the way in UML terms, while not skipping anything in the train of thought? What I plan to do is to let each component provide an Environment class. For the purpose of testing, a TestingEnvironment will gather all those environments and inject the right callbacks into the World. But which types of UML diagrams to use for showing my train of thought, and in which order? My problem is, somehow, that I know what I want to do, but I can't rationalize it to myself why I want to do it that way - I guess it has gotten a part of me to the point I don't make such decisions consciously any more.
What mistakes have I done so far, how to fix them, and most importantly: WHY?
Step 1
(0001_user_commands_use_cases.pdf)
(0002_user_commands_use_cases.pdf)
User Use Cases - Analysis
We are starting with an use case diagram (0001_user_commands_use_cases.pdf) as
part of the preliminary analysis stage. This diagram should only help us
understand what our client wants. These use cases may change over time, but
never the less, they help us understand the system we are going to develop.
Please follow the arrows while reading the following explanations.
The role the user plays when interacting first with our system will be Guest.
The Guest sends to the system some registration data, which we process.
In the diagram, we are describing what happens from a logistic point of view,
from the perspective of the client for which we write the application. We are
not referring yet to classes or objects, or the concrete workflow, which may
end up being quite different.
We are also not dealing with errors, because that would only add more
background noise to the idea - and because error handling does not add business
value to the system. Of course, a system has to be robust to errors, but keep
in mind that we're now just making the analysis - not the design of the system.
Logistically speaking, after the Guest has sent us the registration data, there
is a Registered User. A registered user can send us the login data, so that he
becomes an Authenticated User.
Following the diagram, we can see how only an Authenticated User can send to us
a request to log out of the system, thus becoming a Deauthenticated User.
Why a Deauthenticated User, and not a Guest back again? Basically, it comes
down to us not being able to foresee the future. We really have no idea how the
client will want us to extend the system - nor does he himself! Beside that,
the two roles, a Guest which we know nothing about, and a user which has just
deauthenticated, which we do know something about, are really different things.
Of course it may be that the Deauthenticated User will exist in our application
only for a very short period of time, or it may be that it's created for the
sole purpose of being destroyed back again, without being looked at by any
other subsystem of our ERP - but still I cannot stress it enough: logistically,
the role of a Deauthenticated User stands on its own.
The second diagram (0002_user_commands_use_cases.pdf) contains all the
information of the first diagram, with some added items. If you follow the
green arrows, you will see that the process is the same.
In the first diagram we notice how all three things done by the ERP system are
some kind of generalizations of a process. We have introduced this new generic
process in the second diagram as "Process Data".
Another element we have added in the second diagram is a generalization of the
three "Sending Data" elements. We are not sure how this will materialize in the
next stage (designing the system), but it looks like there's a generalization
there which may, in the end, make the system easier to maintain and extend.
We always seek new generalizations and abstractions because they have this nice
property of making the code maintainable and extendable.
Conclusion
----------
In our design, the ERP will do one central thing, "Process Data", data which
will come in in a standardized way, represented in our second diagram by the
"Send Data" use case.
We don't know how we will design these things yet, until now we have only
thought about the logistics, since we are doing just the analysis.
In the next commit, we are going to look closer at the application domain, in
order to better see the relations between all these notions and to enable us to
come up with a cohesive design.
Step 2
A rough overview of the components in the context of the application domain
Before looking at the diagram, let me first explain the thoughts I had prior to
coming up with this diagram.
We are going to design an ERP. What is this all about, and where lies the
business value in it?
We are supposedly making the product for a company which makes money by sending
products back and forth. This is the core domain of the application.
The basic idea is that a product P is moved from point A to point B, and in
this process, the company makes money.
The application will allow the company to make money by both being good at
organizing the workflow, and by making an existing workflow cheaper.
Let's say a product can be delivered to point C by taking the route A -> B ->
C, or the route A -> D -> C. The ERP must be capable of deciding which one is
better (in terms of money or time), and must manage the workflow it chooses.
So the whole point is supporting the movement of products around. Around what?
Around the world.
For this reason, let's first draw our first component: the World.
In a real project, I would continue by adding a Product component. I've chosen
not to yet, because this project is an educational one in nature, and I wanted
to get across new ideas about the architecture. If I had focused on the
products, I think there would be too much noise for the type of audience this
project is aimed at, a noise which would make the process of creating the
architecture and the architecture itself harder to understand.
We will introduce a Product component later, when the reader will feel at ease
with the architecture which, at that point, he will feel familiar with.
Instead of focusing on Products, we are going to focus on Users. This is,
incidentally, also the subject of the use cases we have drawn in the previous
commit. I think that any reader can relate to the concept of User.
So let's draw that component, the User.
Architecturally speaking, it's not that much of a difference between an User
and a Product. Instead of having Products moving around, we will have (for
now), users who live in the World and take different roles while interacting
with our system.
In the previous commit, we have concluded that there would be a standard way of
passing in data from the User to the system.
In terms of DDD (Domain-Driven Design), the data that gets into the system is
packed up as a Command. Since it looks like a good name, let's make a port of
the World component called Command Hub which will be used to receive and
process Commands from the outside.
Conceptually, the Command Hub will provide a Command Interface which any other
component can implement in order to send commands to the Hub, if it needs to.
We also want to make the components decoupled and thus reusable in different
types of applications, or to be able to combine them. This is achieved by
making an event-driven architecture.
For this reason, let's give the World component an Event Hub, which will
provide an Event interface.
But what will stich together these components?
This question is so pressing, because it's so central to our architecture, that
we will have to further investigate it.
This is not an asnwer, just a comment that would not fit into the comment box
(1) if you want to "teach your readers" something you can't do yourself, it is strange approach. Show your readers the way which works for you. Show the way "your train of thoughs runs", show the the mental images you saw in your mind, paper/pencil/photo in whatever visualization language you use
(2) you're skipping the uml-diagrams: UML 2.5 Behavior Diagrams as not useful? Even the uml-diagrams.org: UML Interaction Overview Diagrams not useful?
(3) this question looks like too broad(long) perhaps opinion-based and perhaps off-topic much more suitable for https://softwareengineering.stackexchange.com/help/on-topic
(4) for the recommended modeling workflow look at
(4.1) Cangnus's sequence diagramming comment in Stack Overflow: sequence diagram for books exchange
(4.2) BobRodes's use case scenario comment in Stack Overflow: Formal language for UML sequence diagrams
(4.3) Scott W. Ambler's Agile Modeling: Where Do I Start? for what when how and why Agile Modeling Best Practices
to name just a few
In conclusion of this my long comment, I don't see any mistakes you did. If this is description of how Test-driven Development and Domain-driven design works for you so well that you even use it subconsciously then it is just fine and ok. Being a good teach is not an easy task, good luck