We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Escaping the Big Ball of Mud

00:00

Formal Metadata

Title
Escaping the Big Ball of Mud
Title of Series
Number of Parts
96
Author
License
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Over the years I’ve seen many implementations of an n-tiered application with an anaemic domain model. You know the kind, presentation, service, data access layer. This architecture whilst quick to start building has its problems. Over time, as the application becomes more complex the codebase will become more and more difficult to maintain, often resulting in a ”Big Ball of Mud”. In this talk I will present some ideas that address these maintainability problems using a solution with a domain model and command patterns to give you a clean extensible architecture. Further to this we will explore event sourcing and cqrs and the benefits they can bring. At the end the you will have seen an alternative way of building business applications, especially those with complex business logic.
OSI modelEnterprise architectureBusiness modelStrategy gameString (computer science)DecimalPhysical lawRight anglePoint (geometry)MUDEnterprise architectureSoftwareService (economics)Cartesian coordinate systemFood energyTwitterPhysical systemMultiplication signSoftware maintenanceSoftware developerCodeWeightMereologyPattern languageMilitary baseGreatest elementPresentation of a groupStrategy gameClient (computing)FrequencyBitTerm (mathematics)Data miningCausalityFinite-state machineLine (geometry)Arithmetic meanBusiness modelAsynchronous Transfer ModeObservational studyMechanism designSequelMobile WebECosDependent and independent variablesPersonal digital assistantLogicInteractive televisionValidity (statistics)Level (video gaming)Different (Kate Ryan album)Touch typing1 (number)WebsiteServer (computing)Complex (psychology)Computer animation
OvalService (economics)Time domainBusiness modelObject-oriented programmingCartesian coordinate systemWeb 2.0Business modelObject (grammar)Service (economics)Multiplication signCodeCausalitySpacetimeLevel (video gaming)Domain nameLogicSocial classEnterprise architectureFunctional (mathematics)Formal languageDatabaseSoftwareLimit (category theory)Group actionLatent heatTerm (mathematics)Strategy gameObject-relational mappingReal numberInternetworkingDemo (music)Representation (politics)MUDValidity (statistics)Physical systemDifferent (Kate Ryan album)GodMassSystem callRight angleType theorySoftware frameworkElement (mathematics)Musical ensembleProper mapCASE <Informatik>ArmComputer programmingWordArithmetic meanOffice suiteLie groupThomas BayesComputer animation
Hill differential equationRepository (publishing)OvalExecution unitTwin primeWindowConvex hullMaizeInformation managementConstructor (object-oriented programming)Business modelValidity (statistics)Execution unitUniform resource locatorCategory of beingOpen setDomain nameCartesian coordinate systemObject (grammar)Invariant (mathematics)Exception handlingDifferent (Kate Ryan album)Service (economics)Physical systemBitNumberMathematicsMUDElectronic mailing listLogicState of matterSoftware frameworkPosition operatorSocial classInformationType theoryStrategy gameTouchscreenCodeMereologyPressureSystem call2 (number)Video gameKey (cryptography)Insertion lossLine (geometry)Temporal logicMessage passingComputer clusterView (database)Discrete element methodArithmetic meanMusical ensembleOnline helpWeightWaveClassical physicsSoftware developerMoment (mathematics)Valuation (algebra)Multiplication signComputer animation
Execution unitHill differential equationExponential functionOvalMaxima and minimaRepository (publishing)Convex hullMessage passingSocial classPhysical systemGoodness of fitMessage passingConstructor (object-oriented programming)MereologyCASE <Informatik>Group actionBitIdentity managementService (economics)Domain nameWeb 2.0Cartesian coordinate systemSeries (mathematics)CodeStandard deviationObject (grammar)Type theoryEqualiser (mathematics)Client (computing)Computer configuration2 (number)InformationDifferent (Kate Ryan album)Single-precision floating-point formatSlide ruleLogicRight angleValidity (statistics)DecimalPattern languageInterface (computing)Regulärer Ausdruck <Textverarbeitung>Dependent and independent variablesCentralizer and normalizerExecution unitString (computer science)Pairwise comparisonVideo game consoleUniform resource locatorHierarchyAddress spaceSystem callForm (programming)Line (geometry)Hash functionBusiness objectDressing (medical)DatabaseProcess (computing)Network topologyReal numberConnectivity (graph theory)BuildingRule of inferenceMultiplication signMathematicsFlow separationMoving averageWaveMultilaterationState of matterCheat <Computerspiel>Principal idealMetropolitan area networkArithmetic meanOntologyDemo (music)FamilyComputer animation
Data managementTraffic reportingEvent horizonService (economics)DatabaseFrame problemPhysical systemRule of inferenceLogicNumberControl flowBusiness modelFlow separationSet (mathematics)Line (geometry)Different (Kate Ryan album)Customer relationship managementBitMathematicsObject (grammar)Wave packetSystem callInformationPoint (geometry)Formal languageCodeComplete metric spaceRight angleContext awarenessRepresentation (politics)MassHierarchySheaf (mathematics)Group actionCASE <Informatik>Field (computer science)MultilaterationCartesian coordinate systemImplementationCodeDomain nameAddress spaceValidity (statistics)Software frameworkPattern languageEvent horizonRepresentational state transferBusiness objectEntire functionComputer animation
Event horizonProjective planeComputer configurationMultiplication signBitSeries (mathematics)Heegaard splittingSocial classPoint (geometry)Service (economics)DatabaseSynchronizationObject (grammar)Moment (mathematics)Message passingDatabase transactionContext awarenessPersonal digital assistant
Demo (music)Business modelView (database)Disk read-and-write headAsynchronous Transfer ModeQuery languageDomain nameWritingDuality (mathematics)Open setEvent horizonAxonometric projectionPrice indexMessage passingBitQuery languageEvent horizonPoint (geometry)DatabaseBusiness modelView (database)Multiplication signPhysical systemNormal (geometry)State of matterSocial classRight angleRepresentation (politics)WritingLatent heatData storage deviceInformationPersonal digital assistantBuildingMathematicsSource codePosition operatorSet (mathematics)Musical ensembleDirac delta functionReal numberForm (programming)Group actionDependent and independent variablesMultilaterationNumberGoodness of fitLink (knot theory)WebsiteWeb pageRevision controlObject (grammar)Home pageConstructor (object-oriented programming)Traffic reportingCartesian coordinate systemFlow separationOrder (biology)CodeSingle-precision floating-point formatSystem callTable (information)Domain nameConsistencyTime zoneTouchscreenSurjective functionMultiplicationReading (process)WeightFreewareSoftware bugPattern languageServer (computing)Term (mathematics)Row (database)Serial portCategory of beingStructural loadLoginRepository (publishing)Boss CorporationComputer animation
Information managementOvalSmith chartLipschitz-StetigkeitMessage passingConvex hullBusiness modelWaveDifferent (Kate Ryan album)Event horizonBitCodeObject (grammar)State of matterProjective planePhysical systemSynchronizationNumberCausalityInformation technology consultingPlanningRule of inferenceTable (information)Validity (statistics)InformationRow (database)EmailMathematicsInstance (computer science)DatabaseObservational studyLogicWeb pageMetropolitan area networkDevice driverCASE <Informatik>View (database)Parameter (computer programming)Home pageSystem callDomain nameLibrary (computing)Vector potentialData storage deviceMultiplication signCartesian coordinate systemReading (process)Source codeVideo game consoleSource codeComputer animation
LogicEvent horizonMotion captureSource codeCodeTime domainBusiness modelLocal GroupDomain nameGoogolVideoconferencingBitVideoconferencingEvent horizonEnterprise architectureBusiness modelCartesian coordinate systemService (economics)Online helpDomain nameSingle-precision floating-point formatGoodness of fitProcess (computing)MUDGreen's functionCodeChemical equationCapillary actionBlogRoutingSource codeContext awarenessDataflowComplex (psychology)Series (mathematics)CASE <Informatik>LogicImplementationCentralizer and normalizerDifferent (Kate Ryan album)Line (geometry)MereologyPhysical systemRight angleAreaGoogolReading (process)Flow separationFreewareGroup actionObservational studyWebsiteVideo gameGodMusical ensembleImage resolutionComputer animation
JSON
Transcript: English(auto-generated)
I just want to get something out of the way first. You may have noticed that I pace a lot. There is a cable right behind this desk. There's a good probability that I'm going to trip up in it at some point and knock myself out. Just feel free to leave and push your way out. That'll be fine. Don't worry about it.
Somebody can help me if they're also feeling kind. So this is the title of my talk, Escaping the Big Ball of Mud. It's not the greatest title, but as everybody knows now, when you do anything in software development, you have to make mention of at least one buzz term. Mine's big ball of mud. So that's why that's in there.
And the idea behind this talk came to me about, I don't know, six months ago, where I started going into a lot of software houses, and I started seeing the same thing.
I started noticing that they were all using the same patterns and building their applications in the same way. These are .NET houses, to be specific. I'm a .NET developer, so the code I'll be using is .NET. And because they're all building their applications in the same way, I started noticing they were all falling into the same trap. When their application got above a certain size and a certain complexity, it was becoming maintainable.
It was becoming a mess. It was becoming a big ball of mud. And I thought it would be useful to give a talk about some patterns and some ideas that are out there that will hopefully alleviate this problem and stop you guys falling into the same trap.
I'm covering a lot of subjects, and I'm not going to be covering many of them very deeply. But the idea was, I didn't want to give you guys a silver bullet. I didn't want you to go away and say, right, I'm going to build my application in exactly this way, because that never works. Silver bullets never work. But I just wanted to highlight these ideas.
So you're probably going to have a lot of questions at the end. And that's OK, because like I said, the intention is that it's just to highlight these things so you can go away and look into them in more detail. And I've got some resources at the end where you can take these things further. So this is me. My name's Matt. I've been a software developer for too long,
by about five years too long. That's my Twitter handle. If you want to get in touch with me, by all means do. This was a picture my friend drew of me, as you can probably tell by the hairline a long time ago. I may need to reduce that a little bit so people recognize me.
So let's define what a big ball in mud is. Now, I'm pretty certain we've all heard the term, right? Who's heard the term? Yeah, everybody's heard the term, right? And if you go on Wikipedia, it's actually a reasonably good definition, which for Wikipedia is pretty surprising. And it defines a big ball in mud as a software system that lacks any perceivable architecture.
It's basically used to describe a code base. It's so complex, so bloated, dependency hell, it's become unmaintainable, right? And one of the original articles actually compared it to Ashanti town now. Now, Ashanti town is one of these villages that pop up on the outside of cities because the cost of living's got too high. So people build their own huts, their own shacks,
and they build them with no infrastructure or no architecture in mind, and they basically become a sprawling city. And the only way you can really sort them out is unfortunately by knocking them down and rebuilding them. Which is often where we're left when we come to the same situation in our code bases.
Now, there's one architecture in the .NET world in particular that I've seen time and time again, and it's a major cause of this problem. It's this one. Who recognizes this? Yeah, everybody recognizes. Who's built an application like this? Yeah, everybody, yeah, me.
Me, too many times. And the idea is you have a presentation layer that is some kind of API, or maybe it's a mobile client or a website. That interacts with the service layer that contains your business logic, your validation, ways of interacting with the various objects. We have a data access layer at the bottom that is responsible for working
with some kind of persistence mechanism, SQL Server, RavenDB, something like that, right? And this is pretty common in .NET. I mean, you have this thing on the side. It's called a business model. And I say that in a looser sense, because it's not. It normally ends up looking something not dissimilar to this. Now, as part of this talk,
I'm gonna be writing some code, and I'm gonna be using an application I used to work on which dealt with trading energy on the stock market, or buying and selling energy on the stock market. And the way the system would work is you would have an agreement with a customer for a period of time to buy and sell the energy. That agreement would have a strategy which would be high risk, low risk,
something along those lines. And then each time you purchase some energy on the stock market, or you sold some energy on the stock market, you would trade the deal ticket, and this would represent the trade. This deal ticket would have a price, how much you paid for it. It would have an amount, how much you bought, and it would also have a status which would be open, closed, canceled, executed.
There was a lot of different stages, but for the simplicity of this demonstration, it was only gonna be free. And the problem with this model is there's no direct way of interacting with it. As I said, you always end up with a service layer
that looks something like this. You end up with a class for each object within your model, an agreement service, a strategy service, and a deal ticket service, right? And they'll have methods upon them so that you can interact with the various objects. I've also noticed something else recently as well. It seems like software applications have been limited
to only using four verbs, get, create, update, delete. I don't know why that is, but people seem to have just kind of fixed upon those words, those terms, and can't use anything else. So this architecture is one of the big problems I see.
And one of the big causes of falling into a big ball of mud. So what are the downsides of this? Well, because you're using a service layer to interact with your objects, there's no way of containing it. You'll often find, and I'm sure because you've all put your hands up and said you built this, you end up with validation logic spread throughout your system. You end up with duplicated validation,
sometimes inconsistencies, multiple ways of creating the same object, but some invalid, some not. And because there's no way of actually centralizing the creation of these objects, you end up with a sprawling mess, you end up with a dependency hell, and it just becomes a problem.
So the aim is that I'm hopefully gonna show some ways to get around that. But it's not all bad. Bad architecture is still valid. If your application is pretty simple, if you just wanna build something quickly, then it's still good. It's still good. If your application doesn't have any behavior in it at all,
then by all means, go ahead and use it. It still works really well. But I find the problem is that whenever you start building a new application, it's always simple when you start, right? It's always that simple. So you always pick a nice simple architecture like this. And the problem is a simple architecture like this doesn't accommodate a complex problem. Unfortunately, you need
a slightly more complex architecture. And I'm just gonna come right out and say, the reason I feel this architecture has become so prevalent and so much of a problem is ORMs. And there's been so many demonstrations and so many one-on-one examples, posted on the internet of people using with ORMs.
And instead of actually creating a proper business model, they try to use the Entity Framework entities as their business model. And that's not always good. It causes you problems where you have to represent your business model exactly as it is in database. That kinda limits you.
So how do you get around this problem of having various service layers, various service classes with the business logic sprawling all over the place and causing an absolute mess? You use this thing called the domain model. Now, who's heard of object-orientated programming?
Everybody? Yeah, there'll be some functional guys, I'm sure. Domain modeling is just object-orientated programming. And it's the idea that you create a web of interconnecting objects that represent something meaningful within your domain. Now, the difference between object-orientated programming and the domain model is
it kinda takes the idea a step further. And the idea is to represent something real world, to represent the business and how a business works. And you want to be using a term called ubiquitous language, which comes from domain-driven design, DDD. The idea behind ubiquitous language
is that you need to come up with a language in which you and the business owners can communicate. It's quite a complex subject. You could talk for days about it and people would argue about it. But at a very basic level, it's just as simple as saying, if they call it a last name,
you call it a last name in your code. If they call it a surname, you call it a surname. You use the terminology that they use to represent the model. And also, rather than just having something like, in our case, the ability to update a deal ticket, if you speak to a business owner or somebody who's actually involved in the business,
they never update the deal ticket. That's not how they speak. They will perform an action upon the deal ticket. They will do something specific with the deal ticket. So that's kinda what I'm gonna hope to demo. So it's demo time. Now before I show a demo,
have you ever been in a situation where you've been in your office and you've been typing away and you've been happy in your own space and somebody will come up and they'll stand on your shoulder and all of a sudden, you completely lose the ability to type. Your fingers turn into sausages. You can't do anything.
Standing up on stage in front of all you guys is like that times a thousand, okay? So I'm completely gonna screw up. I will make massive mistakes. I'll probably sweat, cry, and run off. But if I don't, hopefully I'll be able to show something useful. It's the worst thing. I can already feel my fingers tensing up
to do some exercises or something. Right, this is always the hardest bit as well. Can people see that? Oh wow, I did it first time. Switching between mirroring and extending is like my biggest fear as a speaker. Right, let's make this a little bit bigger.
Can people see this, the code okay? Bigger again? Is that gonna work? I'll tell you what.
How's that? We winning? Great. The black screen okay? I figured it was a dark room. And we're all developers, so black screens are always good. Okay, so here's a simple application that represents the model that you saw in the slides.
We have an agreement. Which has some basic information on there, customer ID, start date and end date, and it has a strategy which we talked about. The strategy, very simple, just contains an idea name and maybe some notes. And then, also on the deal ticket we have,
oh sorry, also on the agreement, we have a list of deal tickets. And as I said in the slides, deal ticket represents a trade on the market. And then we have this thing called the deal ticket service. Now this, this is a culprit. Let's minimize this down. This is where all the problems start.
And it's got two methods upon it. It's got create deal ticket, and it's got update deal ticket. I seem pretty innocent. Let's have a look at the create deal ticket method. Get rid of that. So, state, there we go. So this method does a number of different things. The first thing it does is it checks to make sure
the deal ticket is being created with status of open. Because that makes sense. You don't want to create a closed deal ticket. That would be silly. The next thing it does is it makes sure that the deal ticket has a price, or a price greater than zero, and it also has a currency. And if that's okay,
it checks that it's got an amount and also that it's got some units to go with the amount. And if all that works out, then we try and save the deal ticket. Now this has got a number of problems. The first one is this validation, this logic is only temporal.
Once you get to line 30, there is absolutely nothing stopping you doing something with the deal ticket to put it in an invalid state. You can change the amount to be minus 2,000. There's nothing stopping you to do that. As developers, I'd hope you wouldn't make that mistake, but I'd kind of like not to leave that opportunity out there. The other problem with this
is that this validation is specific to this method. As soon as you want to create a new method for validating this, to create a deal ticket in a slightly different way, you have to take this validation and put it somewhere else. You have to copy and paste it. Okay, we don't do that. We actually create a validate method or a validate class, or use some framework to help with a validation.
But even that still relies upon the user knowing to call the validate method. Now in a simple application, you're gonna remember that stuff, but once your application gets above a certain size, you forget this stuff, it gets hidden in the noise, and that's why math's basically how you end up with a big ball of mud. So how can we avoid this situation? Well, we can take all this validation
and we can put it in a constructor. Constructors aren't just for dependency injection. You can actually use them to construct objects
in valid states as well. So we need an ID, we need a price, currency, and deal ticket.
Use the magic of Reshaper. Who uses Reshaper in here? Who could code without it?
I wouldn't have a clue what I was doing. Stick with validation logic in there. I'll just clean this up like so.
Now all of a sudden, we're in a position where we can no longer create a deal ticket in an invalid state. All the validation is there. It doesn't matter where you use it within your system. You always have to use this constructor.
It's always gonna be valid, which is great. In fact, we can take it a step further. Because we're making use of the constructor, we know, as we mentioned earlier, the deal ticket always has to be open with a status of open. So we can just completely get rid of this check
and just set this to, so that's a lot neater. So that's the create method sorted with. That's, we're fine. We can't, you know, the validation has put into, been put into a central location. There's no chance of it sprawling out over the system because it's right on the deal ticket.
The next problem we've got, is the update deal ticket method. Now these are always worse. The creating is always as simple as a modification, which is always a difficult part. The reason I feel it's always a difficult part, or at least what I've seen in my experience, is because you always try and do too much within a single update method.
You always try and encompass every single way of updating that deal ticket in a single method. And that's what's happening here. Excuse me. So the first thing we check in at the top is, has the deal ticket been executed? If it has, well, we don't wanna be allowing users to modify the data. That makes sense.
If a deal ticket has been canceled and we're trying to reopen it, then we only allow the status to be changed to reopen. And then otherwise, we've got the same validation in here for checking that the price and the amount are valid. And if that's all kosher, we change the data.
Now, we could do what we did with the constructor and we could just add an update method to the deal ticket class and put that logic in there and therefore, the deal ticket class would be protected. The deal ticket object would be protected. But as I was mentioning, the whole idea behind the domain model and domain modeling
is that you try and represent the business. And as I said, they don't update a deal ticket. Nobody updates a deal ticket. They'll execute a deal ticket. They'll reopen a deal ticket. They'll modify the values of a deal ticket. So wouldn't it be better to create individual methods on the deal ticket?
So we can just say, just look away for a second, and I'll say, I'll type status. Yeah, because there's no validation logic around it.
So we just have an individual method on the class for executing it. And we can take this further and further and we can talk to the business owners and we can figure out how they want to actually use a deal ticket and we can add the methods. So we have a bit of Git magic.
That's the bit I've been panicking about. So now we've added methods for being able to reopen the deal ticket. We've added a method for being able to cancel the deal ticket. And another method for being able to adjust the costings. So this is good now. We're starting to add some behavior to our model.
You know, we're starting to make it work like the business expects it to. And we're starting to encapsulate all the business logic within the single model. Now this is good. This means now we can't modify the object in a way to put it into an invalid state. Its invariants are protected, with the exception that our properties have still got public status.
So we just need to make those private. OK, cool. So we've got a safe object. We've still got another problem here in that we've actually still got validation logic that's
duplicated, which is this. And it's duplicated again with the adjust costing method. Now what can we do about that? We could pull it out into a separate method and have each method call it. That'd be quite nice. But we can also make use of something called a value object.
Now a value object is a small type that's equality isn't represented by its identity. So in the case of a deal ticket, you know if two deal tickets are the same because they have the same identity. In the case of a value object, it's the actual object that denotes equality. A good example of a value object would be a person's name.
Now a person's name isn't just represented by a first name as a string and a surname as a string. You need to have both unless you're Bono or Cher or awkward like that. So wouldn't it be better to represent that as its own type?
It'd be great. Have the validation logic contained within it. And it can be used throughout the system. So in this domain, there's a couple of examples of this. We've got a price and a currency. Now one without the other doesn't make any sense.
You can't have a monetary value. It makes no sense without a currency. It couldn't mean anything. If it's great British pounds, it's probably worth nothing. If it's a Norwegian kroner, it's probably worth a lot, especially when you're trying to buy beer. Yeah, that's been hurting this week.
So let's add a class to that. And we'll call it money. We just have a simple constructor where we take a decimal value and a string currency.
Maybe it's private. And then we can take this logic and just add it in here.
So now we've got a money object that we can use throughout our entire system. It can only be created in a valid state. OK, the logic's probably a little bit weak. You might want to check various different currencies
or something like that, but it's just a demo. And it can't be modified once it's been created. And then within the actual deal ticket, we can just get rid of the currency object and just have a price.
That wants to be of tight money. So now we've pulled that logic again into a central location, and it's reusable throughout our system. It's quite neat. If there's one thing that I would ask people to take away from this talk, it would be the use of value objects. There's so many cases where you can use them. In the UK, an address is not just several strings,
several lines of the address. It's an entire object. And there's rules around it. We have a thing called the post code, which is part of the address. And it's got certain rules, but it has to confirm form to a certain standard. So creating a value object that has a regular expression for the post code as part of it. So you can always make sure you're creating
a valid post code. It's great. It also means, and this does happen, that when you're calling a method like the deal ticket constructor where it's just taking native types, you can't accidentally pass the wrong value in because it has to be a money object, which is quite nice as well.
So there's another one here. We've got the amount in a unit. Again, an amount is nothing unless you know what the unit is. 1,000 of what? I don't know. So hopefully, we'll try Git again.
OK. It worked. It keeps working. So we've now got a quantity object. The same principle. The validation is a part of constructor. But I've also added in and overridden the vehicles method
so that when you're doing a comparison between the two, it takes into consideration the value on the unit, right? And then the hash code as well. So that's hopefully created a nice componentized central location for your business logic. And obviously, this is just one class. The fact of the matter is, you're going to have a hierarchy of classes.
But you can componentize it and build it together in the same way. So what's this done to the service layer now?
OK. So now that we've moved all of the logic out of our service layer, it's purely a facade now to allow our client to interact with our domain object. And there's just a series of methods for executing the deal ticket, reopening the deal ticket,
canceling the deal ticket. And all it does is pull the relevant deal ticket out of a database, perform an action upon it, and then save it back, which is quite nice. It's looking good now. But there's still a problem with this, as far as I'm concerned. And that is, as more and more ways of interacting
with the deal ticket as the system grows, this class is just going to go crazy. And it's going to become huge. I've easily seen these things creep into 400, 500, 600 lines of code. And that's not great. Because more often than not, when your client
takes a dependency upon the service class, the service layer, it probably only needs to make use of a couple of the methods, probably more often than not, just a single method on the class. Yet you're making it take a dependency upon the class that's got so much more information in there. And you could argue that it's breaking the SRP principle,
single responsibility principle. So how could you fix this? Well, one option is you could use interface segregation, which is the idea that you have an interface to represent each method upon the class. And then this class implements every single interface. Still kind of cheating, because ultimately you've
still got this concrete class, so it's going to balloon and become a mess. So another option would be to create a single class for each method. It sounds like a lot of work, but it's not actually that bad. And this brings me around to something called the command handler pattern, or the command pattern.
And the idea with the command pattern is that you have a command which represents something, an action that you want to perform, and the information required to perform that action. So here we've got create deal ticket command.
And then within your system, you need a way of being able to perform that action. And that's where the handler comes into play. Red lines, that's not good. We'll pretend they don't exist. So here we have a create deal ticket handler,
and it takes that command, and it handles it. By handling it, it just does what it did in the service layer, creating the deal ticket and then attempting to save it. Now this might seem like you're breaking your code up a little bit too much, but it does also introduce a really nice way of interacting with your domain.
But on the client, if I can find it, my console application, it means with a little bit of infrastructure code, because I've made use of a generic I handler method, we can actually take a command and just dispatch it. And with a little bit of infrastructure code,
we can match, marry that command with the right handler and execute it. So if you build in an MVC application or a web API, your commands, your actions will just end up dispatching the message and then maybe doing something with a response. So it's a little bit tidy, right?
Have we got any thoughts, opinions so far? OK. Nobody's thrown anything at me, so we're good. Right. Let's go back to the slides for a second.
OK, so just to reiterate quickly over what we just showed there, we showed, hopefully, the problem with the service layer and encapsulating your business logic within the service layer. We showed how it can lead, hopefully, to the business logic getting sprawled throughout your system and becoming inconsistent. Then we showed how we can bring it actually
back into a domain object of some kind and try and reflect some of the business rules within your system within the domain model. Then we looked at value objects as a way to represent small pieces of information which can be reused throughout the system. And then we looked at swapping the service layer for the command handler pattern.
Now I know I'm kind of whipping over and going over this stuff rather quickly, but like I said at the start of the talk, the intention is to kind of just highlight these things to you. So if it looks interesting, it looks possibly like something that might benefit your system, then you can go away and you can look at the implementation details in more detail.
So but that's not the final solution. I know, because we're only halfway through the talk. You'd best not be. There's still the possibility that your domain model is going to just balloon. Some people in Michigan will be building incredibly large, complex applications. And if you try and model all that in a single hierarchical model, you're
just going to end up with the same mess. It's just going to end up being a complete and utter disaster. So it's at this point that you maybe want to consider breaking your application down into a number of services. Now buzzword number two, microservices. Just throwing it out there. There's a number of different ways you can break your application down. Microservices is one. I've heard some very strange rules
about how you should size your microservices based upon lines of code. For some reason, 300 lines of code is the magic number. If you go above that, you need to create a new service. I don't know, man. That seems crazy. But anyway, but ultimately, you want to be looking for logical separations in your code.
Now there's a domain-driven design goes into a whole field with chapters and books and entire talks, basically, on the idea of breaking your model down into a number of different contexts. And they argue that it should be broken down based upon the ubiquitous language. So if you've got different departments within your company that address or treat, I don't know,
a deal ticket as a separate thing, it's a different term, then they use that as an excuse to split it up. Yeah, there's valid reasons for doing that, but basically looking for logical breaks in your code. So in the case of this example, the system was actually a lot bigger. We had customer relationship management sections.
We had trading. We had compliance. And we had reporting. The key thing about breaking your application up like this is you need to make sure there's no dependencies between each of the domains. Because if you get to a situation where compliance, for example, has a database that trading calls directly into, then you're introducing a dependency
that means you can't change that database without affecting another context. And that's when you start to get into a bit of a dependency hell. Trust me, it's happened. But they do still need a way to communicate. So in our system, when a deal ticket is finally
ultimately executed, we need some way to tell compliance that that's happened. Because that's when compliance are interested in the system. Before that point, they couldn't give a monkey as to what was going on. But once a deal ticket's been executed, they need to know about it. Now, again, there's a couple of different ways
you can do this. You could have each of your services expose HTTP API, some RESTful API. Microservices talk about doing that. You could also make use of some durable queuing, frameworks like n-Hibernate, n-ServiceBus, and mass transit, and built around MSNQ or RabbitMQ.
But we need some way of being able to communicate. And a nice way of doing this is by raising events. So the idea being that once a deal ticket has been executed, you can raise a deal ticket executed event that you can then distribute to anybody who's interested. You can publish that event. And everybody's interested, and you can go, right,
I want to do something with that. All right. Try and switch again.
OK. Now, for simplicity, I've kept it all within one project. But again, the whole idea of splitting your service up into multiple contexts, you've kind of got all those advantages that everybody's ranting on about at the moment, which is they're independently deployable,
independently scalable. There's been plenty of microservices talks on that recently, at least. But like I say, the idea has just been able to communicate between the two. Now, there's one thing that's always, I've seen, catch people out in the past. And I just wanted to highlight that quickly, because it's a bit of a gotcha. And the idea, and it's all about
when you dispatch this event. Now, I've seen this done a lot. The idea that within the execute method, you would have a static class that allows you to dispatch an event. It's all fair and well, but it does suffer from one problem, which is at that point when you dispatch the event,
you're assuming that the deal ticket is going to get saved to the database. For some reason, it doesn't, and it fails. And you're going to get out of sync. So the better option is to actually collect a series of events on the object. And then at the point when you save it,
ideally wrapped in a transaction, dispatch the events at that point. Because then you know it's saved. You know it's guaranteed. And you know you're not going to give any inconsistencies. Just something that I've seen happen quite a few times.
Now, there's one problem with the domain model and building a domain model like that is that it's only really ever intended to be used for writing to the system, to be executing actions upon the system. You never want to use that model, that object for reading.
That would make any sense. It's got loads of behavior on it. You've already made use of constructor and made the properties private, which makes serialization a little bit tricky. So ideally, you want to be taking your application a step further and looking at something called CQRS. Has anybody heard of this term CQRS? Lots of people.
OK, we're good. So CQRS takes something that's known as the CQS principle, which I'm going to get it wrong. But it's command query. I have a separation or segregation. I can never remember. Close enough. And that idea is that a method should never both execute and read information at the same time.
It should either execute something, perform an action, or it should read something. It should never do the same thing, because then you're going to get unintended and unknown behavior. Command query responsibility separation, CQRS, takes it a step further and says you should be using separate objects, separate classes for reading and writing. And you end up with something that looks a little bit similar to this, where
on the left-hand side, you've got the command facade, which talks to your domain model and uses a repository to save you information to some kind of persistence layer. It's kind of what we just created. But because that's not suitable, then you want to create some query facade that
allows you to query your data in a way that is suitable. And you've got some kind of read model, some kind of view model, and a basic data layer that calls down your persistence. Again, this is probably something you've all done. You've probably made use of the view model pattern to construct an object in a way that is suitable for a specific page on a website
or a specific API. Maybe using link, if you're a .NET developer, using link to construct these queries. That's great. That's nice. But it's not always good, because as we know, link queries can become an absolute nightmare and become sometimes very hard to optimize. And that can really slow down the reads.
So wouldn't it be nicer if rather than aggregating and reducing your data when you're wanting to read, you do it when you want to write? And you end up with something that looks like this.
The idea being that you write to an independent star of some kind. And you use that write model to then produce a denormalized representation of your data specific to certain needs.
A very basic example might be you'd have a denormalized set of data for the index page in a website and another one for a details page, a report, or something like that. So you can have these multiple read models. But again, that brings the problem. And as soon as you start denormalizing data within your database, you've got the risk of inconsistency.
As soon as you have that data represented in two separate places, you're going to get into problems. So you need to have one thing. You need to have what's known as a single source of truth, basically. One place within your datastore that is guaranteed to be correct.
Now, there's multiple ways in which you can do this. But I'm going to use it as a tenuous link onto something called event sourcing. Who's heard of event sourcing? Everybody's heard of event sourcing. That's good. So the idea behind event sourcing,
I'll come on to it, actually. So in our example, if we look at how we might interact with a deal ticket, this is kind of a workflow. Again, massively simplified. But you may open a deal ticket. You may adjust the quantity. You may close a deal ticket. You may get opened again, and so on and so forth. This goes around for a bit.
And it looks something like this over time. Now, there's a problem with the way we currently store data. In most systems, we store the state at any point in time of the data.
If we were to work on this deal ticket object, and in 90% of systems, we get to the end, to the point where the deal ticket has been executed, we're going to be losing a lot of information about what's happened to that deal ticket over time. And looking at an example here, once it gets executed,
if you only store the current state, there's no way you're going to know that the deal ticket was closed at some point. Now, you could use an audit log, and you could use, if you're using SQL Server database triggers, but don't ever do that. Please. Horrible things.
So the idea behind event sourcing is rather than storing the state, you store the deltas. You store the events that have happened to the deal ticket. It's not quite on the screen. A real world example of this, if you think about GitHub, I imagine most people here use GitHub
or some kind of version control, right? It doesn't store the state of your code. All it does is store the changes that have happened to your code. And when you want to get your current code, it takes all of those changes, and it plays them in order until you eventually end up with the final state. Well, that's the same idea with event sourcing. Rather than storing the state of the deal ticket,
we store all the events that have happened to the deal ticket. And then when we want to modify it, we take all those events, we replay them, and then we add or execute another event, another command against it, resulting in another event through event store away. Now this brings with it a few advantages. The first one being, you've got an audit log. You've got an audit log for free.
And you've got one that's guaranteed to be right, because it's also going to be your right model. This is what you're creating as first point of call. It's not a secondary concern anymore, which if you have to deal with any systems that involve compliance, you'll really appreciate it. One of the other problems with audit logs actually is when you use things like database triggers
to create a new row in the table and so on and so forth, you store the change and how the data is changed. But what you very rarely store is why that data was changed. Using an event store, you also get that advantage, because you know the event that's been executed. It also brings with it another advantage. But it's a very good source of truth in which to build all these multiple read models off.
So you get something like this. So when our system, the deal ticket, executes, we can take that event and we can use it to construct a read model in any form we want. We can also, because we've got all the events,
at a later point in time when our boss comes to us and says, how many deal tickets have been closed, use it to very easily construct a new report, just by looking at the deal ticket closed event and building a report based upon that. And it also brings an advantage that because you have all
the events within your system, it also allows you to rebuild your application to any point in time. If somebody wants to say, what were things looking like yesterday? We had a problem yesterday. Something went wrong. Can we go back to how it was yesterday? You can pull the event store down, replay to a certain point in time, and you've got the system exactly how it is. I actually used that to fix bugs before, which is quite useful.
So how does this look in code?
So it changes the way the system works slightly. And it changes the way you have to work with your domain
model slightly. And there's a real gotcha behind this in that sometimes people don't like it. And it's that you have to separate the behavior of your system, the validation rules of your system, away from the code that
actually modifies the objects. Now, the reason you have to do this is because when you replay this event store and you want to rehydrate, effectively, your object, you can't have it fail. Therefore, any business logic in the methods that apply these events to the object cause the potential for it to break.
So you end up with code that looks like this, if my mouse would stop moving. So when we reopen a deal ticket, we'd have a method, just like before, to reopen the deal ticket. But rather than executing or changing
the state in the same method, we want to change it in a separate method, which we call apply. Now, there's some infrastructure code that sits behind this. But basically, it's very basic in that it just matches the event which gets raised with a method called apply and passes that in as the argument.
And like I say, the reason you do this is because when you rehydrate the object, you're using these apply methods to rebuild the object. And let's see if this works. Oh, no. That was all right. There's always a hesitation when it starts pulling down NuGet packages that everything's going to go wrong.
So if we look at the console application we've just run, I'm dispatching a number of different commands. I'm dispatching create deal ticket. I'm canceling it. I'm reopening it. And then I'm executing it. And if you look at the console application, you can actually see what's happening. So in the first instance, we're saving the deal ticket.
People see that OK? Does that need to go bigger? Make it a bit bigger. Is that any better? OK. So you can actually see that the deal ticket created event has been saved. Then when we want to modify it again,
we call out the rehydrate deal ticket event and to rehydrate the object into the state that it currently was. Then we execute another event against it, which is we cancel it. Then next time we rehydrate the object, you can see it's replaying both those events. Again, then we add another event to the end of it.
And again, finally, call each of those three events that have already been saved against the system and finally execute the deal ticket. Now, I mentioned that this can be really good for building out the projections, your read models. OK? So as well as using those events to rehydrate your object, you can also make use of them in another handler
that just listens to that event. In the case here, we've got a projection to build an index page for a view model for the index page.
And we handle each of these events independently. And we use that to pull information out. This is RavenDB, incidentally. So when a deal ticket created event appears, we add the new row in. Then when we handle the cancel, then we modify it and so on and so forth.
And yeah, so like I say, it's a really nice way of being able to build up read models. And as I said, if for any reason your read models get out of sync, you can just replay all the events and you end up with the state that you're currently in. I know I've kind of glossed over a lot of stuff for never is a lot of caveats in using event sourcing.
And it can be quite complicated. But the idea was I just wanted to just show a little bit of what it does so that you might go, hey, that's interesting. I'll go looking into more detail. Incidentally, to store the events, I'm using a tool called Event Store or Get Event Store, which was built by a guy called Greg Young.
But there's libraries out there and frameworks out there that allow you to store the event as just basically rows in a SQL table. So there's libraries to help you with this storing and rehydrating of events. But the code genuinely isn't overly complicated.
OK, so I've got a bit of a conclusion. Then there's a little bit more to come after that. Like I said, the idea of this talk wasn't to give you some silver bullet, some way in which you can solve all of your ball of mud problems. It was just to show you some patterns, some ideas that I use that help separate the application up
into logical areas, separating reads from writes, separating the model from interacting with model, and all that good stuff. So we talked about building a domain model in a way to prevent code duplication and keep the business logic centralized. We then talked about breaking the application down a little bit into different contexts
to still further prevent your application from becoming this big monolith. Then we talked about CQRS, separate your application flow into reads and writes, so the two aren't the same percent. And then, like I say, we talked a little bit about event sourcing and how that works.
But this is all reasonably complex stuff. And if you do go down this route and you do start looking into these ideas, it's a goddamn minefield. It really is. And it's fun, and it's good.
But it ends up with your application actually having quite a complex architecture and quite complex processes. So you shouldn't always use it. In fact, you can use it in part of your system, or you could use it in the entire system, which is not necessarily always the case. The problem I've seen, and the problem we have,
is that often we try and use a simple architecture to solve a complex business problem. And that's where we go wrong. But equally, and I see it a lot more recently, but we try and use a complex architecture to solve a simple problem. So it's all about getting the right balance.
It's all about finding the right architecture for your code. So you might not use all of these ideas. You might use some of them. Just use what's relevant. But for the love of god, don't go away and implement all these things for a blog. It's not needed. It's not needed. I'm yet to see a blog that makes use of microservices. I don't think we're far off on nanoservices or pico
services. We're going to end up with services that are just a single line of code, I can only assume. OK, so if you do want to take this further, there's some resources. There's two really good books. These are kind of like the de facto books on domain-driven design. There's one by Eric Evans, which is a slightly older book.
It's called The Blue Book. And then there's one called Implementing Domain-Driven Design, which is by Vaughn Vernon. That's called The Red Book. Both are excellent books, both worth reading. There's a Google group, which I highly recommend going on. It's incredibly active. There's a lot of people on there, and there's a lot of incredibly smart people, way smarter than me, that are willing to answer
your questions and help you investigate these ideas. There's a great tutorial series by a guy called Greg Young. And he's kind of like one of the godfathers of event sourcing. It's a paid video.
But seriously, if you get the opportunity to watch any videos by Greg Young, I highly recommend it. And finally, I came across another talk by a gentleman called Gibby Bogart, which he did at NDC a few years ago, called Crafting Wicked Domain Models. And he did it at NDC, I think, in 2013.
That's actually quite a good video to go away and watch. And it explains more about domain modeling and some other techniques you can do to create a centralized business logic. And that's it. So I hope you've seen some ideas that you like. And I hope you're going to be interested enough
to go away and look into those things. And I hope you enjoy the rest of the conference. So thank you. If you have any questions, I'm just going to stand at the front here. So by all means, when you filter out, just feel free to come up and chat with me. Or completely ignore me and apparently
only the green button works. I was told to let you know.