Nice one, Derek, appreciate it. The biggest thing I learned watching your videos is that almost any challenge is about managing coupling and cohesion. Thanks!
Conceptually I understand, but we hand-waved over state in the handler. How would we be handling this in a system where my service receiving the event isn’t stateful, e.g. the service is scaled out and one instance handles the order placed event and another instance handles the order billed event? We can obviously store that in a db, and then I guess we just check that for each event? Wondering what patterns for that are typical I guess.
Would be interesting if you make another video with some opinions how to best integrate an external workflow system like Camunda - especially when you want to have a centralized dashboard where you can see which workflows are in progress and in which state they are.
If I have a scenario of long running task that I receive the request externally (An API) and run in background and asynchronous using a queue (or some sort of messaging technology), is this still a command right? Because my command has not been processed yet and I'm just putting the overall processing in other thread avoiding my client waiting
In the end the goal is to be extendable and runtime decoupled from different systems/contexts. We use temporalio for process orchestration which has benefit of writing "sync" code but still be runtime decoupled and easy extendable.
In MassTransit, It makes you feel the coupling even better, because to publish you just say "_bus.Publish(event)" but when you want to send a command you first have to know the queue name....then you do a Send.
I know this is only tangentially related, but you showed an example of a saga, where there can be two concurrent event triggering the saga at the same time. In case both events really _do_ come at the same time, and are being processed in parallel, they both see an initial state of the saga (nothing has been received), both update the respective piece of state, and they both finish thinking the flow is not complete, essentially breaking it and making it unable to continue without a manual intervention. Multiple solutions come to mind - use a mutex on the handler (dunno if C# already doesn't come with such magic), but that doesn't help with multiple replicas on different machines or processes - use SELECT FOR UPDATE, or a similar database-level lock, but should the repository know we want to update the retrieved model at this time (before commit)? - retry on conflict after the saga fails to be saved (due to other thread saving it first, this requires some WHERE checks on a timestamps or version) Is this something you're usually concerned about? Which one is the most idiomatic in your opinion?
Concurrency. Most messaging libraries, like NServiceBus in my example, only allow a single atomic saga instance to be executed at a time. But yes, the implementation behind the scenes for that can be any of that you mentioned, using locking at the DB level or even optimistic with a retry if conflict.
I just added cross boundary Command to my system. The main reason was that this is triggered by user on UI and it felt weird to have something like UserRegistrationTriggered event so I used command called RegisterUser. Do you have opinion on my reasoning? 🙂
The UI and/or aspects of the UI are apart of their respective boundaries. In other words, a boundary spans client/ui/server. The Client/UI isn't it's own boundary but a composition of boundaries.
@@CodeOpinion This is where n-tier architecture or layers-by-technologies trip people up. A domain boundary is vertical so it includes UI and other presentations layers. I think the .net space does itself a disservice because many of their templates and sample code push organization this way. Even it if isn't obvious, your video shows an example of vertical slice organizations which can promote better boundary design. Great video!
Thank you, good video. I suppose a follow-up would be an introduction of an Anti-Corruption Layer (ACL) to get the shipping address for the order? or rather, how else would you maintain light-weight events but still reduce / eliminate coupling?
I'd say video is a bit confusing. Events and commands can be internal/external (private/public). You can publish events and use commands that represent your context internal business behavior. Those should not come outside. You can also use them (but external version) to communicate with outside world. The only difference is that now, when they're outside, it's infrastructure concern, not application/domain. Example. When order is placed and billed, and those external events are published there might be scenarios: 1) define your external command "ShipOrderCommand" and translate those 2 external events coming in into it (ACL form DDD, stream processing, whatever) 2) define your external event "ShipOrderRequestedEvent" and do the same as p.1 3) define your external command "ShipOrderCommand" and make orchestrator publish it 4) just process in place (maximum coupling) It all comes down to coupling between contexts, teams, processes within your organization and whether you use orchestration/choreography. That's just my 2 cents.
Does this apply to durable computing platforms like Temporal? Where the workflow should live? And the activities? Are the activities just sending commands or doing the task? In your video “Goodbye long procedural code! Fix it with workflows” the workflow cross different boundaries. Thanks so much for such amazing videos
Temporal is a good example of durable workflows but ultimately you still need to define what code goes where and how you choose to invoke it and communicate between boundaries.
I'm c# dev, but i lately learn about gleam. It's fantastic. It's simple as hell, simpler than js, but you can built your codebase app as monolith and it will be fail over as microservices. Your battleneck would be your bad database design And you dont need message brocker, kafka and choose between messages or events because of beam vm concurancy model. if you dont need high aviability gleam anyway would be a good choice for backend due to it simplicity
Nice one, Derek, appreciate it. The biggest thing I learned watching your videos is that almost any challenge is about managing coupling and cohesion. Thanks!
It generally plays key role in a lot of decisions and with it the trade-offs.
Conceptually I understand, but we hand-waved over state in the handler. How would we be handling this in a system where my service receiving the event isn’t stateful, e.g. the service is scaled out and one instance handles the order placed event and another instance handles the order billed event? We can obviously store that in a db, and then I guess we just check that for each event? Wondering what patterns for that are typical I guess.
You could use a distributed cache for storing state for example, or a DB that handles concurrency very well if you have several replicas
Would be interesting if you make another video with some opinions how to best integrate an external workflow system like Camunda - especially when you want to have a centralized dashboard where you can see which workflows are in progress and in which state they are.
If I have a scenario of long running task that I receive the request externally (An API) and run in background and asynchronous using a queue (or some sort of messaging technology), is this still a command right?
Because my command has not been processed yet and I'm just putting the overall processing in other thread avoiding my client waiting
Yes, anything that is about invoking behavior and has some side-effect (eg, state change) is a command.
In the end the goal is to be extendable and runtime decoupled from different systems/contexts. We use temporalio for process orchestration which has benefit of writing "sync" code but still be runtime decoupled and easy extendable.
Good to hear. I was assuming I'd get someone mention temporalio
@@CodeOpinion you should do video about it :p, they have c# sdk as well
In MassTransit, It makes you feel the coupling even better, because to publish you just say "_bus.Publish(event)" but when you want to send a command you first have to know the queue name....then you do a Send.
Good explanation. Thank you
I know this is only tangentially related, but you showed an example of a saga, where there can be two concurrent event triggering the saga at the same time. In case both events really _do_ come at the same time, and are being processed in parallel, they both see an initial state of the saga (nothing has been received), both update the respective piece of state, and they both finish thinking the flow is not complete, essentially breaking it and making it unable to continue without a manual intervention.
Multiple solutions come to mind
- use a mutex on the handler (dunno if C# already doesn't come with such magic), but that doesn't help with multiple replicas on different machines or processes
- use SELECT FOR UPDATE, or a similar database-level lock, but should the repository know we want to update the retrieved model at this time (before commit)?
- retry on conflict after the saga fails to be saved (due to other thread saving it first, this requires some WHERE checks on a timestamps or version)
Is this something you're usually concerned about? Which one is the most idiomatic in your opinion?
Concurrency. Most messaging libraries, like NServiceBus in my example, only allow a single atomic saga instance to be executed at a time. But yes, the implementation behind the scenes for that can be any of that you mentioned, using locking at the DB level or even optimistic with a retry if conflict.
I just added cross boundary Command to my system. The main reason was that this is triggered by user on UI and it felt weird to have something like UserRegistrationTriggered event so I used command called RegisterUser. Do you have opinion on my reasoning? 🙂
The UI and/or aspects of the UI are apart of their respective boundaries. In other words, a boundary spans client/ui/server. The Client/UI isn't it's own boundary but a composition of boundaries.
@@CodeOpinion This is where n-tier architecture or layers-by-technologies trip people up. A domain boundary is vertical so it includes UI and other presentations layers. I think the .net space does itself a disservice because many of their templates and sample code push organization this way. Even it if isn't obvious, your video shows an example of vertical slice organizations which can promote better boundary design. Great video!
Thank you, good video. I suppose a follow-up would be an introduction of an Anti-Corruption Layer (ACL) to get the shipping address for the order? or rather, how else would you maintain light-weight events but still reduce / eliminate coupling?
I've done a video about ACL before (ruclips.net/video/Dok2Ikcjaro/видео.html) but that's more about not leaking or absorbing semantics.
I'd say video is a bit confusing.
Events and commands can be internal/external (private/public).
You can publish events and use commands that represent your context internal business behavior. Those should not come outside.
You can also use them (but external version) to communicate with outside world.
The only difference is that now, when they're outside, it's infrastructure concern, not application/domain.
Example. When order is placed and billed, and those external events are published there might be scenarios:
1) define your external command "ShipOrderCommand" and translate those 2 external events coming in into it (ACL form DDD, stream processing, whatever)
2) define your external event "ShipOrderRequestedEvent" and do the same as p.1
3) define your external command "ShipOrderCommand" and make orchestrator publish it
4) just process in place (maximum coupling)
It all comes down to coupling between contexts, teams, processes within your organization and whether you use orchestration/choreography.
That's just my 2 cents.
Does this apply to durable computing platforms like Temporal? Where the workflow should live? And the activities? Are the activities just sending commands or doing the task? In your video “Goodbye long procedural code! Fix it with workflows” the workflow cross different boundaries.
Thanks so much for such amazing videos
The example of this video is more choreography than orchestration, isn’t it?
Temporal is a good example of durable workflows but ultimately you still need to define what code goes where and how you choose to invoke it and communicate between boundaries.
I'm c# dev, but i lately learn about gleam. It's fantastic. It's simple as hell, simpler than js, but you can built your codebase app as monolith and it will be fail over as microservices. Your battleneck would be your bad database design And you dont need message brocker, kafka and choose between messages or events because of beam vm concurancy model. if you dont need high aviability gleam anyway would be a good choice for backend due to it simplicity
Nice hockey hoodie. You play hockey?
Yup! I'll try and toss in a Bauer one next
@@CodeOpinion Platform agnostic ;)
good video thanks
Glad you enjoyed it