Connell Watkins - - Onion Architecture with DDD and CQRS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
cool hello everyone i'm connor watkins i am the principal platform engineer at oakbrook finance a few of us here we are sponsors um we've been on a bit of a journey over the last couple of years building a whole new platform in well ddd and cqrs and onion architecture so i'm going to take you through sort of the things i've learned and hopefully teach a thing or two so just pre-warning there are going to be lots and lots of arrows in this talk it's diagrams and boxes and it's also going to be well the arrows mean dependency i'm not using uml or any notation really that's just this knows of that because we like to personify things we like to say this doesn't know that that's allowed to happen and contract knowledge the public contract i mean not the internals and there's going to be a lot of fancy animations like that if you saw a max talk earlier and you like them you're going to love this i do apologize if you don't like them um i've got a lot to get through so questions at the end that's okay if time allows i'll maybe stop between these sections so we're going to do 20 minutes of boxes and arrows and then the animations will die down a little bit i'm going to start building up the onion architecture from the from the middle so i'm going to do the domain layer in ddd and then i'm going to do the layer around the outside of that which is the application layer in cqrs and then about 10 minutes at the end of infrastructure very rough that is so i'd like to make a big statement at the start and say software is about drawing boxes not to diminish anybody's he's been doing this for 10 15 years but software is about drawing boxes is the first statement i like to make because we don't just write everything in the main function we like to draw boundaries around things and we like to simplify the problems so they have fewer things to worry about um so here i've got four boxes here some methods i'll draw some boxes around those and i'll call them a class and then maybe i'll say one of those classes or maybe i'll say one of those methods is private within the class because we think these methods are related so i'm going to draw a box around that as well i'm going to call that a class library um and then i'm going to draw another one of those over here and this is the same thing it's another library but this one's got some meaning to it this one's an api and the class in there is we call a controller and the methods in there we call actions some of them anyway so oh yeah and then we'll draw even more boxes we'll draw boxes around that and we call them a solution or service or a component so it's all drawing boxes hopefully i've convinced you it's all about drawing boxes and there's the animation boxes and arrows software is all about drawing boxes and arrows because once we finish dividing everything up we stitch it all back together again with some arrows we say the api layer knows of this library this is dependency remember i'll say this class might know of this class and then can we do that nope can't do that because that would create a circular reference so we have a direction of dependency we say can only go one way this can know of that but that can't you can technically do the same thing with your classes but your code quality tools will probably say that yeah highly highly coupled things together and it's not very cohesive you can do the same with the big big gray box on the outside if you like know each other's urls but again why would you want to do that so hopefully i've convinced you that software is just about boxes and arrows now how many how many people recognize these boxes and arrows they've all worked in n-tier applications surely so in a traditional entire application we've got the three layers presentation business logic data access so again i mean dependency the presentation layer knows of the business logic layer the business logic layer doesn't know that it's part of an api data access puts things in a database but it doesn't know what the logic is above it um and then i want to introduce this second arrow now this this arrow why is it done now just going to put another arrow pointing the exact same way so this arrow is the flow of control this is subtly different so whilst dependency is i know of this thing control is i tell this thing what to do the business logic layer tells the data access to go and put something into a database so it's making the call if i put a breakpoint in my repository and i looked at the call stack i would see that it's being called by the business logic layer which is being called by the presentation layer so it's about calling it's about driving it's about telling it what to do so that's the second arrow now take a look at the the black arrow between the service and repository you're ready for these animations whoa let's go the wrong way look there you go that did you see that not the new arrow that's just appeared on the screen the arrow that the new arrow is pointing at is now facing a different direction let's zoom in like that dependency inversion we've now got the flow of control is still going the same way as it was before my service is still well my business logic is still calling in to my data access but the data access knows of it understands what the business logic layer is so before before we go into why you would want to do this i'm just going to explain how we add an interface this will be your i repository so you've got a repository implementation an eye repository over here and a service that's using the repository there so these are not uml arrows like i said these black arrows here just mean no of like one would be in uml the implementation one would be this just uses that but you're probably used to seeing it over there right in your data access layer where you've got your eye repository and your repository implementation sitting in the same project but all i've done is move it back all i've done is move one code file just want to drink my microphone then all i've done is move one code file and i've changed the direction of the dependency between the boxes on the outside the blue boxes the projects so just just if you didn't get that i've got one more way of explaining that with some code i'm glad these screens are massive i was worried if we were not going to be able to read that so i've got a talk service it's a talk about a talk it's a talk talk and then you got that uses the repository and then i've got my entity framework repository implementation here and all i've done if you check the top bit is draw the boxes in a different place i've not actually changed anything in the code i've just drawn different boxes and now i've changed the direction of dependency so that's the dependency inversion so this is called ports and adapters the interface is a port or actually it's the interface and uh the model that used in the interface is the port and the data access layer is adapting that port it's implementing that interface you can think of the port as kind of like the business logic saying i know how to use this repository i don't know how to implement this repository but it's public and it says anybody that knows how to implement my port please do so please adapt my port and then the data access layer does that um by using the adapter pattern it's an adapter so you might have your yeah i repository and then this repository wraps up another repository it implements one interface and encapsulates another it's the adapter pattern gang of four so interesting to note here is the business logic has no transitive dependencies there are no there's no black line going this way so the business object doesn't even know that entity framework or mongodb we use at oakbrook doesn't even know that these technologies exist you couldn't even accidentally use your stuff from your empty framework libraries like your attributes like if you wanted to say that this object has a primary key that's this property you can't you can't even accidentally do it entity framework gets around that by providing a fluent interface which allows you to in your db context in your data access layer you can point at the port and say this is the primary key from the outside so it supports ports and adapters or that dependency inversion and then this is the other side so the other side note that now my control my controller is still pointing at the business logic so that's still going the same way but if you look all the way over there my controller is pointing that way at mvc my controller knows of asp.net nbc it understands what mvc is because i have to implement it i have to inherit that will derive from that base class and i need to use the http get attributes um but the flow of control is still going the other way it's still going this way my controller or asp.net mvc sorry calls my controller if i put a breakpoint in my controller and i looked up the call stack it's calling me if i put a breakpoint over here the call is all going that way but i've inverted it so yeah there's frameworks do this all the time as the frameworks are obviously inverting obviously obviously inverting the flow of control all the time it's very common thing and it's basically the controller is another adapter now look at the the one in the middle the business logic layer it doesn't depend on anything it's perfectly pure in the middle it has no external frameworks has no extra dependencies no it doesn't depend on any projects at all it's very pure so it's very easy to test because we don't have to write any tests that call into a database or and make an api call it's just c sharp or java or whatever your weapon of choices and that is shapes and boxes and arrows and i'm way ahead of schedule so i'm talking too fast so hopefully i've convinced you that software's all about shapes and boxes and arrows and that is ports and adapters this is one i've liberated from the internet so i didn't draw this diagram and this is the ports and adapters architecture diagram big problem with this is um not enough so all of the dependencies go inwards the api like i said knows of my business logic the database knows of my business logic they've actually got some other examples here as well they've got a console application that calls in and you've got a message queue at the back end there as well maybe http you've got external api calls happening at the back end as well the flow of control goes that way so this is you note this diagram is split into primary slash driving adapters and secondary slash driven adapters they're just two you can they're interchangeable terms i prefer driving because my api is the flow of control it drives the business logic it tells the business logic what to do and then the on the other side the driven adapters the database is being told what to do by the application even though it depends on the application it is being told what to do so it is driven the business logic drives these adapters on the right-hand side here and the flow of control goes this way and that's sports and adapters that supports an adapter's architecture um everything dependencies go inwards flow control goes from left to right that is or i think was originally called hexagonal architecture and swap hands now that one's getting tired hexagonal architecture was the original name and i think somebody said we should call his ports and adapters instead probably because there's there's no reason this should be a hexagon as far as i'm aware it's nothing to do with the number six there's there are six things on here but your your web services probably don't have a console interface as well you might not be using a message queue so the six on here but you can have as many adapters as you want on either side it's a hexagon because we like shapes what's that now it feels uncomfortable we like shapes we like these visual cues we like to think about problems like in shapes we like to visualize them we don't like thinking about all this code put together we put shapes to it so this one's a hexagon it doesn't mean anything not about six so on shapes here is our entire or layered architecture again visual we've got these layers like that i'm not it kind of looks like a cake i'm not doing a cakes have layers joke in an onion architecture presentation no not goodbye i made one olympic rings architecture i thought i was being really really clever here so a couple of years ago i came up with olympic rings architecture it's got five projects dependencies look like this so you could say direction of dependency goes downwards and the flow of control goes from left to right five projects all i've actually done here is if you say those are the same project and they're the same project that's the interior architecture or if you said those are the same project dustports and adapters so i thought it'd be really clever this one's a cone or any geometry nerds no i'll give you his real name the cone is just a slice of this circle of clean architecture what's wrong with this diagram not enough arrows the cone had arrows on it um so the clean architecture dependencies go inwards like your ports and adapters dependencies go inwards and then we finally get to onion architecture which is what i'm going to talk about so clean architecture on your architecture very much the same idea dependencies go inwards in fact i think clean architecture is like a abstraction or a spec and onion architecture is an implementation not what it matters we've got these visual cues it's an onion it's a you like to think about this as a circle you can peel away these layers i can peel away my presentation layer and underneath it there's this application layer that you can see and you can peel that away and there's a domain layer underneath that so you've got this visual these visual things these prompts that help us but it doesn't matter it doesn't have to be an onion it's not about onions and to prove that point i invented castle architecture this is exactly the same thing it's exactly the same dependencies exactly the same architecture the code is completely the same except i've drawn a diagram like a castle i really tried to animate this one i really did i imagine all that coming out and landing in a castle shape but no my keynote skills are not that good so am i just saying in all of this that onion architecture is just the hexagonal architecture the ports and adapters with a domain in the middle yes yes that's exactly what i'm saying we split our business logic into domain logic and application logic but everything on the outside is just the hexagonal architecture except it's not hexagon it's a circle and we call it an onion because it's got layers so split the logic up and now oh what was that oh yeah i'm finished on boxes now so there's all the fancy animations done what time are we on okay i'm going to go into build this now i'm going to build from bottom to the top i'm going to start at the domain layer and you can imagine me sort of doing these building blocks and i'm going to build outwards and build up a sample of onion architecture does anybody have any questions on shapes and boxes by the way before i move on to the main bit ahead of time what's your favorite shape oh come on hexagon onion is that shape no no cool all right let's have a look at the domain layer then so this is a very very boring diagram call it the flag of japan architecture if you like there's no no um dependencies no arrows it's like our business logic imports and adapters very pure it's in the middle very testable very easy to write units around this there's nothing going on it's very simple and now we're going to have a look at what it's all about boxes we're going to have a look at what's inside this circle and we're going to use domain driven design to animation as well so what is in domain driven design the building blocks of domain driven design then we're going to find in our domain layer we're going to find aggregate routes and we're going to find entities you might have heard of them domain events there's some of them in there oh and there's value objects oh but stop just making sure you're still awake i'll board you with shapes so this is the biggest mistake everybody makes i've made it i i don't think of anybody i've seen anybody not make it domain driven design is not about aggregates and domain objects and value objects and all that stuff it's about it's business first it's domain it's in the name it's designing your software driven by the domain so it centers around this domain model it actually says the structure and language of the code should match the business domain that's what domain driven design is about so it's not about all this technical jargon it's actually about ironically not using technical jargon so you don't use http client or whatever provider all of these nice technical words we try and talk in the same language as the domain experts it's about a collaboration between us the technical experts and the domain experts there will be people that work for your company that aren't technical so at for example we worked on a payments um domain with the finance team they're experts in payments so they are the main experts in that example and we work together to develop a ubiquitous language ubiquitous language is what we all agree we're going to use to talk to each other so this is the stuff that you're going to put in your requirements this is the stuff that you're going to use in your meetings this is the stuff that you're going to write on the board and using your standups in the morning it's the stuff that the same language everybody's going to talk to each other in and we all agree that we all know what these terms mean build yourself a glossary of these terms define them i went to a um scott veloshin talk in ndc before in the before times and he had this example in f sharp where he said he was doing dvd and he's got this f sharp code this domain code and he showed it to the non-technical domain expert and the domain expert says yes that's how i understand the domain that's how i understand it where's the code there you go that's the dream that's what we want to strive for i know in c-sharp we've got to use keywords like public and class and static but if you strive for yeah having code that your domain experts can read then they can do rpr's so there's a whole culture around domain-driven design it's not it's not a technical thing it's a whole it's a whole everybody needs to buy into it and i was going to then refer you to lucy's talk who would have been talking about the culture of domain driven design after this slot unfortunately lucy can't make it so i can't so i get to talk to you about the main events you know that you know when i said the first mistake everybody makes is doing the technical bits first well i'm going to do that i'm going to talk to you about all the technical bits so the main events something has happened some state has changed in our domain now these are written in the ubiquitous language and over here i've given this side enough these are written in our ubiquitous language so something happened so let's get some examples we've got account registered event payment made event talk started event these are the main event examples uh they're using our ubiquitous language i forgot what i was gonna say next oh yeah there is a bit of culture in this actually i will go on about this to come up with these there's a process called event storming where you can sit and talk to your domain experts and just get them to tell you a story just get them to say what happens in their domain because when they're describing their domain they're going to say things like oh yeah somebody makes a payment they don't create a payment they don't add a payment or anything like that they say they make a payment or they take a payment so you use this language in your domain events oh one thing to note here is i'm using init only properties the c 9 feature because domain events are immutable once i've created these i cannot go and change them they are immutable objects that cannot change once they've been initialized for example the the talk started event there i can't go back in time and change the speaker as much as you all might like now you can't go back and change who started to talk the speaker object itself also would need to be immutable i know that these are written in past tense they've already happened it's not this talk starting from them so i'm going to i'm going to try i'm going to try another diagram this is going to be a sequence-ish diagram so the big red circle remember the flag of japan is the domain that's the domain layer and then from outside the domain somebody says do the thing it tells the domain so the it sends a command in and it knows of what the thing is so the thing outside the domain references the domain it understands what the domain is and it sends a command and says go and do the thing so the domain goes off and does the thing and then it says something has happened the domain event something has happened and it publishes that event that goes out it doesn't know who's listening doesn't know who's listening to the domain event because the listeners or the subscribers they before the domain event happens they come along and say i want to know when something happens tell me when something happens so they understand what a domain event is but see see where i'm going with this the arrows the flow of control is going out of the domain but the dependency goes into the domain so domain events like ports and adapters are just another way of inverting the control i accidentally clicked entities and value objects are two other forms of objects you're going to find in the domain layer these are very distinct have reference equality value objects have value equality and by that i mean if you have two objects two entities that have exactly the same properties in every way and you compare them they're still not equal they're still different objects whereas a value object if they had the same properties in every way and you compare them such as an address you've got the same street same city same postcode and you go are they the same address yes they are the same address so they're a value object they equate by value entities are mutable value objects are immutable like the domain events you can't change a value object it is what it is but an entity you can go and change you can change its properties and it can change over time so a value object has no lifespan it just is what it is whereas an entity has a history and it is that history that is the domain events when things happen an entity changes that's a domain event aggregates now this is an aggregate am really really knowledgeable about cars by the way so this is an engine it's another box another box um you've got a door these will be entities because i'm bad at cars but there's a thing called a v8 there's a you can have two v8 engines that are the same in every way but they're not the same actual engine you can put them side by side and well they're not the same in the same type of engine so these are value objects same with the door you can have those fancy doors that open sideways they might be the same type of door but they're not the actual same door same with the lights you can have those horrible yellow ones wheels mirrors but the color the color is a value object or maybe in this domain it's a value object because if you had two red cars let's stick with red we're in the domain layer if you have two red cars and then you took their color property and you compared them and you said is this the same color or are these the same color yes these are the same color they're both red so they equate by value so the value objects and we draw another box we draw a box around all this and we call it an aggregate the car is also an entity so if you were thinking like your sql tables you've probably got seven tables here and the car would be an entity just like the others but the car is the aggregate route it is the way in so the aggregate provides a consistency boundary so the aggregate is where you can put your domain rules you can say a car must have four wheels or maybe three but not two that's a bike you can put other domain rules in there which i'm not going to go into so the aggregate route is like the way in the way into the aggregate the only way into the aggregate so if i was outside of this box and i wanted to make a change to the wheels i wanted to slash the tires of the wheels maybe i can't just say slash tires from wheel one two three i have to say i have to know the car id to know the wheels to go in maybe a more appropriate example would have been like you know driving a car is a bit more appropriate isn't it driving a car you don't drive each wheel individually you drive the whole car itself so that's an aggregate and here's some code this is how you might choose to implement an aggregate there's no real rules this isn't even exactly how we've done it at oakbrook either and i said a car is an entity and it's also my aggregate route and then i've got some properties here i've omitted the constructor and some other properties and then i've got this method paint body i'm using the ubiquitous language so it's not set color or any set body color let's paint the body and then i've got some domain rules if the color is yellow we don't like yellow cars if there are any lights nope you can't paint the body because they're already lights installed and you're going to paint your lights and then i do actually set in the color and then i add the domain event so i add a body painted event and i initialize some properties there note that i'm adding it to a collection here i've got like a domain events collection it's not i'm not actually publishing it at this point in time which i'll get to later so that's an aggregate and then you've got data abstractions and these are abstractions so this isn't my database remember ports and adapters these are like your ports saying if anybody knows how to do repositories please implement them my repository is just like a bucket where i can put aggregates and it is aggregates that i put in the repository not entities so i can't go and put wheels i don't have a wheel repository i can have a car repository where i put the whole lot i found this um easier to grasp after working with a document database so working with mongodb i just put the whole car in with an array with the wheels it made a lot of sense to me when we say these can only be aggregates maybe maybe the concepts there combined so that's a repository you add things to it you get things back out the unit of work is a an abstraction for a database transaction so i can make as many changes i like to my repository i can add seven different things but they don't actually happen until i commit that work until i say all of this work that i've done that's a unit and i'm doing it at once i either do it or i don't and then your data restrictions now i'm still talking about the domain layer now this is a mistake we made at oakbrook as well these it's really easy to consider these outside of the domain it's really easy to consider these technical things um so we put them in the application layer at first i like to sort of think about it like this it's kind of in a really thin repository layer just outside the domain and dependencies go inwards so the repositories are they know of my aggregates but my aggregates aren't going to be committing units of work or they don't know of my repositories so it's like a thin layer just on the outside and that is the domain layer so i'm going to talk about the any questions on ddd domains or anything like that before i move on to what's outside of that none silence i'm either doing really good or really bad outside of the domain layer then we have an application layer and an onion architecture dependencies go inwards so we've implemented this as just a domain layer so it's the application project references the domain but you can kind of see it's like going all the way through and how we're going to do it cqrs these are going to be the boxes inside our application layer so what's in in the application layer then cqrs commands and queries that's it again it's kind of in the name command query responsibility segregation we are segregating the responsibilities of our commands and our queries before i do cqrs cqs came first basically says a method or a function can either read or write but not both definitely both and that's it if you're writing if you're making changes you're having side effects for the thing you're calling return void or maybe a task if you're doing it asynchronously and if you're reading something don't make any changes no side effects if you're returning data so you can read as many times as you like and you know you're not changing the system so that's cqs and cqrs add in the responsibility segregation takes that a step further and says rather than just functions or methods entire subsystems we're going to separate entire subsystems for reading and for writing half of our application layer is concerned with the commands the writing half is concerned with reads the queries the key point here is it's not crud you're not putting in the same object i think i've dropped my clicker for this so we made this mistake in i think we might still do because we're designing rest apis we're putting rest apis in our ui layer and i think when you think about a rest api and you think about that state you think put my http the put i've got an object of this shape and i go put that at that location and then i kind of expect i'm transferring that state to be able to go and get that same object back in the same shape when i do the read later that's like thinking like a rest api i think and that is not cqrs that is in fact the opposite of cqrs in fact eqrs says one thing and it's don't do that so i think rest apis make that quite difficult to think about if we're designing rest apis around cqrs instead think task based when you're doing your command side think what is it i'm actually trying to do stop thinking about an object that you're putting somewhere or you're changing think about the task that's at hand and when you're doing your read side think about what it is you want to read think about your view model think about what it is you're trying to get what data am i interested in here unless cqrs it's split those subsystems into two and i'm going to do another weirdish sequence diagram this time with a big blue circle remember that's the application layer the people blue circle is the application layer and then from outside we say verb something they're all verbs so like make a payment start the talk so we verb something and then inside the command we've got a handler we've got a bit of code this gets quite messy this and i do apologize in advance we've got a handler which is actually in the blue well lives in the blue circle and the handler says go and load me the aggregate it goes repository.get so it's talking to that pinkish layer that repository layer says go and get me the aggregate from the repository and then i'm going to go and do a thing i'm going to mutate that aggregate i'm going to call maybe paint the body if it was a car and then i'm going to commit my unit of work i'm going to say i'm done making changes now you can save that transaction or that is my work commit and then i return fulfillment and that's it that's the command handler now i'm returning fulfillment there remember commands don't return anything commands don't return certainly not any business data note my return type there i'm returning a task i could also throw an exception though so you can return errors from a command or at least we have decided we you could actually return say you've got a command result object that's got information about the command execution so you can return the fact that the error is there you can return an error message i'm sort of told with the idea maybe you can return like the number of rows that were affected i don't know kind of like a non-query and sequel don't know that one so you can return fulfillment or error there is another school of thought if you've got like a command bus if you're using rabbitmq and you're doing everything asynchronously putting your command on the queue you haven't actually committed the unit of work or you haven't actually done the thing it hasn't been handled so instead you return acceptance you would say or acknowledgement and you say i will get around to that it's like an asynchronous 202 if you talk in http status codes so the key key point is you don't return read models don't return anything from the query side these are entirely separate subsystems okay so i can do several changes within a command handler i can do two mutations on my aggregate or three or four not limited do as many things as you want and the idea is because i can only return fulfillment or error i can't return well i only did half of it so that's the way the unit of work comes in if do a thing that first mutation fails oh succeed sorry and then the second one fails the exception is thrown i return an error and the first thing never happened in the first place because i never committed it so i i've either done the command or i've not done the command but there's no i did some of the command because then you can leave your application in an inconsistent state which isn't what you want you can kind of think of the blue circle the application layer is that transaction boundary i do so from outside of this layer you send something in and you don't really care there's a transaction going on you just get back it's successful or not you don't really care how many things are going on remember i said the main events get raised when you mutate things in aggregates so they get raised so you have to dispatch those domain events because remember i just added them to a collection so your command handler or your command side of your application is responsible for dispatching the domain events and then you might have domain event handlers in the blue circle that make even more changes then they perhaps they even change other aggregates that's quite controversial that they can change other aggregates if you want maybe you don't want so those last two things there they're committing the unit of work and event dispatcher you probably don't want to write every command handler to do these things at the end so we actually decorate our unit of work so you can't commit it without distracting the domain events i like to put these in a pipeline like middleware so you can have both of them say we dispatch the events and then we commit the unit of work and then you don't have to think of them so that's commands let's do queries the whole the other side was it that side i can't remember where i was metaphor now queries the read side so similarly you say get me something it is verb something again by the way because maybe you can ask to read something provide something but they're queries you'll recognize what they are when you see them but get me something and then similarly to the commands we've got a query handler looks very similar handling a query i'm returning something this time because remember queries are about getting me data they're not have to make changes and you probably know what's coming next right oh no maybe you didn't see that one coming now there's no red circle we are returning a projection we're returning a projection i'm actually here going straight to the database doing a little query and projecting onto that something object because you don't want to use your aggregates or your entities over here firstly if you return those people can mutate them and this is the read side you don't want them making changes secondly just cqrs says they're whole different subsystems my aggregates and entities are used for the command and the writing subsystem queries i want to have their own system i want to make them their own thing so i return a projection this something object is defined in this part of my application you could even use um i think view models you could do a join here maybe you'll use an entity framework for your command side and you're using dapper to do a join on your read site and you're joining across seven tables to return a result maybe you have the freedom to do so because you segregated those responsibilities you could even use a separate database you could have a database for your commands and you can have a sql database for your reads it's called a read store so whoa whoa whoa whoa whoa animation as well by the way did i just say i drew a big blue circle around a red circle and then i divided the blue circle in half and i said you can only use the red circle in half of it that's silly well we debated this a lot at oakbrook we wondered well what about value objects can you return value objects in your query results if i wanted to return a sort code i've already got this object there i've got the dependency on the domain so i can use it can i just return a value object now if you google that ask the question can i use ddd value objects in my cqrs queries you come across a stack overflow post with two answers one says yes one says no and they both have the same number of upvotes so i don't know we went with yes because we thought why would i want to re-implement this object but i think when it gets to the point where we need to separate them and we realize there are separate concerns reading sure we'll start using a different object but we do use value objects look value objects in our queries there's another one though there is a reason there is a reason to depend on the domain and that is event sourcing remember domain events so this is my read site my query side of my application is listening to or subscribing sorry to those domain events and then when it handles them it builds up that read store remember i mentioned the read store in fact remember i said you could do a join across seven tables well what if what if only one person can do rights they've got permissions and you've got a million queries a day doing this join across seven tables but you're only writing in the morning it seems very inefficient you might want to optimize your query side but you don't even need to touch your command side you just change it from doing a join to seven clear seven tables to when the command happens we'll pick up the domain event that happens off the back of the command happening and then we start building that table ourselves and we'll just select the rows from it so we'll do the joins when the command happens we'll make a denormalized table with all the data that we need one thing to know here so black arrows are dependency domain application commands and queries both to point inwards but the flow of control is remaining going left to right command comes in changes stuff the main events go out that way so hang on i kind of lied a little bit earlier didn't i said there were two things in the in the application and then i went on about the main events a lot so there are yeah there are domain event handlers within an application they're not really the public part of the application i guess commands and queries are things that you can send your application but the main events handlers they happen inside internally there are two and a half things you can do in a domain event you can write to a read store as i was just saying this is all within the same transaction by the way so when i was building up that table it's all committed as part of the same transaction the blue circle is the transaction boundary and my application project so i can write to a read store here when the car gets painted um i'm building up a little stacked database that i'm going to sum later or remember i can make further changes so i can reduce the amount that's in my paint can and then maybe that one maybe that reduce throws an error because there's not enough paint left and then my original command would fail and transaction doesn't get commit that's a whole separate class and the other half thing that you can do is schedule jobs now the reason i say half thing is because that's kind of a query all you're really doing is building up a read store like a table of jobs and then later a job process is coming along from the outside and sending queries to your application layer saying have i got any jobs to do have i got any jobs it's polling so it's kind of a half thing because it's kind of queries but i wanted to give it a special mention so now my colleagues at probably looking at me funny because we can't do that we can't schedule jobs within the transaction and i'm going to say we do we do it in every service but we just have a non-abstract version we have a very concrete job scheduler which we call the event stream so we have what is in essence a generic domain event handler that picks up every domain event and serializes it and puts it in another collection that's a read store it's the event stream and then we have a job processor that sits outside of the application layer that picks up all of those events and sends them off to an event bus we use google pub sub you can use rabbitmq use whatever you want oh you can use that to build up a read store as well remember i said you can use a sql database we do we use that to build up a sql database we handle those those events the integration events now but i'm getting ahead of myself you're still with me that's cqrs no nobody's there with me though i've lost your that was the application layer cqrs these building blocks how they all fit together the infrastructure layer onion architecture points inwards dependencies go inwards so these are the things outside our application layer that are sending the commands and queries to our application often divided into two so you will often see an onion architecture diagram that looks a bit more like this in fact the one i showed you earlier was divided into three the infrastructure layer actually had a presentation sort of part to it so this is the flow of control this is ports and adapters again we've got our driving adapters over here on the infrastructure layer and we've got our driven adapters over on the right hand side sorry presentation layer infrastructure there remember check that out we've got controllers they send things into the application they're driving and the database that's the driven adapter that's on the infrastructure side of things and remember in portland adapters we also had external apis we could make third-party api calls outside they're driven the application you might have um so actually this is where i think the whole flow of control thing breaks down and i know i've spent like 45 minutes banging on about it but when we talk about presentation and infrastructure right my external apis are making calls without that project is making external apis perhaps my application layer declares a port that's the money sender it goes i money sender it doesn't know how but it just needs to send money and my external api layer is maybe going to use paypal to do that or the project so he's going to use paypal and yeah i'm going to make some http calls over to paypal's apis to send some money but then later paypal needs to well it doesn't need to but i might want to handle web hooks that come from paypal so they're going to call me and i wouldn't expect those calls to go right the way around here and then come into our presentation there into our controllers just because they're controllers i would expect the class library that's responsible for dealing with paypal and translating paypal to be doing the paypal stuff so i would expect the the models that understand what paypal is to all live in that one class perhaps this is a misunderstanding that i think most people go through and i've definitely gone through where you think these boxes are c-sharp projects but maybe they're not maybe they're just things on a diagram and you can have different boxes for your structure for your project structure but i think everybody looks at application and domain and they make c-sharp projects from this so i'm going to say they are c-sharp projects and say this is the paypal project that understands paypal and it also understands my domain so it is the job its job is responsible for translating paypal's language into my ubiquitous language that's called an anti-corruption layer in ddd i'm not going to say again because it's a horrible word anti-corruption layer so it's the project that's responsible for translating another language into my own so if paper if paypal can send me web hooks into my infrastructure layer and the flow of control is going that way now then i'm also going to put integration events over here in the presentation layer whoa why has he put integration events in the into in the presentation there i've not seen a diagram on the internet it does this and the reason i think this is these are your public contracts these are what people see when i see the word presentation that's what it's about right this is i'm presenting to the world i'm presenting my service to the world and this is my the public bits these are the internals like our classes from the first flight and then i see this these are the things i'm going to put in my documentation and the flow of control that goes into the controllers and it goes out through the integration events so i've no longer got my big yellow arrow going across so if paypal's on that side doing the same thing something calling me is seeing this side check this out you can have two services where one of the external apis sees the presentation layer of another service and that's anti-corruption layers here's another service and now all of a sudden when i'm looking at this sort of scale the arrow there is about dependency and not control i have a direction of dependency but the flow of control between these services is going both ways i can zoom out and i can have loads of services and maybe these are all on your architecture but you might have different architectures and different services and i've got like a flow of dependency all the core services that don't depend on anything are over on the right hand side and all the things that depend on them are to the left i've got like a big architecture diagram and you can draw even more boxes around that but i'm not going to go into that that's probably a bit too much my name spaces or domains right that's it that's all i got except i'm going to try and give you the tldr or didn't listen you ready for this so from outside i call a controller in my presentation layer that sends commands to my application layer which load aggregates from the domain via repositories which are implemented in the infrastructure layer as a right store my aggregates contain entities value objects and raise domain events which are handled in the application layer by domain event handlers which may make further changes to my domain or write things to a read store which can later be queried by resilient jobs such as publishing integration events writing to an external read store or calling external apis thank you very much i've been connor watkins please do find me on social media i've got no followers because this is my first talk i've ever done
Info
Channel: DDD East Midlands Conference
Views: 22,865
Rating: undefined out of 5
Keywords:
Id: CdZzfqwnx4I
Channel Id: undefined
Length: 50min 22sec (3022 seconds)
Published: Sat Nov 06 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.