Your SlideShare is downloading. ×
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
The CQRS diet
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

The CQRS diet

5,843

Published on

Presented at Conferencia Rails 2010 …

Presented at Conferencia Rails 2010

You probably follow the motto “skinny controller, fat model”, which is a good thing. Skinny controllers are definitely the way to go but, as your domain logic grows, it’s very easy to end up with a model overweight problem, which is not so good. The issue is that your ActiveRecord objects have to deal with too many different responsibilities: validation, business rules, awareness of its own persistence, filtering…

Command-Query Responsibility Segregation (CQRS) is an architectural pattern that promises to improve the maintainability, performance and scalability of your applications by helping to separate concerns in your system. This pattern, along with Event Sourcing and other Domain-Driven Design ideas, is gaining increased popularity, particularly among developers building solutions for rich / complex domains.

This talk will introduce these concepts and show different ways we can (re)architect our Rails application in order to get their benefits. From a simple implementation using just the “tools in the room” to a implementation “in all its full glory” using NoSQL data stores and messaging queues.

All those who have a Rails projects complex enough to abuse of model callbacks, de-normalize the database or use presenter objects, may get benefits in terms of simplicity and maintainability from implementing the ideas I’ll present in this talk.

Published in: Technology, Health & Medicine
2 Comments
25 Likes
Statistics
Notes
No Downloads
Views
Total Views
5,843
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
203
Comments
2
Likes
25
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. ★ the CQRSdiet ★ Make your modelslose weight By LuismiCavallé 1 Hello, everyone! Tonight I’d like to talk you about CQRS…
  • 2. ★ the CQRSdiet ★ COMMAND-QUERY RESPONSIBILITY SEGREGATION 2 CQRS stands for Command-Query Responsibility Segregation which is a design pattern behind a really…
  • 3. ★ the CQRSdiet ★ SimpleIdea 3 …simple idea. Let’s take for instance a typical Rails model…
  • 4. ★ the CQRSdiet ★ Project Report Project find scoped delete save 4 In Rails any request regarding Projects is handled by the same ActiveRecord model Be that request a find, a filter, a save or a delete, the same model will be used..
  • 5. ★ the CQRSdiet ★ Project Report Project find scoped delete save QUERIES COMMANDS 5 Well, the idea behind CQRS is the creation of two objects were there was previously one. The separation occurs based upon whether the methods are a query, meaning that they ask to read data, or a command, meaning that they ask to change the state of the system.
  • 6. ★ the CQRSdiet ★ Simpleidea enables interesting things architecturally that 6 As you can see, the idea is quite simple and not especially interesting in itself What makes it interesting is the architectural possibilities that it opens.
  • 7. ★ the CQRSdiet ★ Simpleidea enables interesting things architecturally that 7 Some months ago, observing how these ideas were gaining traction in other development communities, particularly among developers that call themselves “architects” and work in the enterprise, which I thought it was really cool… or maybe not… well!, anyway, the thing is that I became interested and started a personal research about these topics. This talk is about sharing my findings. We will explore this pattern along with a few related concepts and discuss some different implementations with their benefits and pitfalls.
  • 8. ★ the CQRSdiet ★ WHYSHOULD I CARE? ™ 8 So, let’s start with the motivations…
  • 9. ★ the CQRSdiet ★ Areyouhappywith your Railsapp? ★ Modelscleanandstraightforward ★ Noneedtodealwithcomplexdomain logic ★ Noscalabilityorperformance issues ★ Nomaintainabilityproblems 9 The question to ask here is “Are you happy with your Rails app?”. Because if your models are clean and straightforward –probably because you don’t deal with complex business logic–, if you haven’t got important scalability or performance issues and you don’t feel like there’s too much technical debt in your code. Then you probably don’t need to care about separating reads and writes or any other architectural alternative. But…
  • 10. ★ the CQRSdiet ★ Somesmells… ★ Denormalizeddatabase ★ Complexcaching ★ Overweightedmodels ★ Presenterpattern ★ Increasinglycomplex infrastructure ★ ConsideringNoSQL 10 …if you’ve got a denormalized database, or there’s caching logic spread all around your app, or your models are way too big and complex, or you are using the presenter pattern, or your infrastructure is becoming increasingly complex, or you are considering NoSQL solutions. Then, chances are that you’ll find CQRS ideas interesting.
  • 11. ★ the CQRSdiet ★ THESINGLE RESPONSIBILITY PRINCIPLE should neverbemore onereason class change There than fora to “ ” 11 You know? There’s this Object-Oriented Design better practice that you may already know. It is called “The single responsibility principle” and it states that one object should only have one responsibility (or, put otherwise, just one reason to change). But if you look at an ActiveRecord model you see a totally different story…
  • 12. ★ the CQRSdiet ★ ★ Validation ★ Domain Logic methods&callbacks ★ Structureassociations ★ Filtering scopes ★ Presentation helpermethods ★ Persistence ModelResponsibilities 12 In Rails, every of your models has to take care of: Validation, Domain logic (typically hooked to persistence callbacks), Structure (every model declares its associations), Filtering, Presentation (sometimes its convenient to put that presentation logic in the model instead of in a helper) and, last but not least, every model is aware of its own Persistence. Wow! definitely our models got more than one reason to change. Which is ok, as we mentioned earlier, when there’s no much complexity to deal with. But when that’s not the case, this Rails’ monolithic approach makes hard to partition our problem.
  • 13. ★ the CQRSdiet ★ theCQRSdiet 13 I think it is clear that we need to put our models on a diet. The CQRS diet!
  • 14. ★ the CQRSdiet ★ Project Report Project COMMANDSQUERIES 14 Let’s take it from where we left it. The idea was quite simple: Commands & Queries are handled separately by different objects. The interesting part is the architectural decisions that this enables. So let’s explore those interesting things.
  • 15. ★ the CQRSdiet ★ Project COMMANDSQUERIES Project List Project Details 15 The first and one of the most interesting things you can do in a CQRS architecture is having two separated data models and making different choices on each of them regarding things like how data is structured or where and how it is stored. It turns out that, given the different purposes of each data model, these choices can be quite different.
  • 16. ★ the CQRSdiet ★ DOMAIN DATAMODEL READ DATAMODEL ★ Validation ★ Business Logic ★ Filtering ★ Data presentation CONCERNSATRIBUTTES NORMALIZED PERSISTENCE IGNORANT TRANSACTIONALCONSISTENT OBJECT ORIENTED ACID DATA ORIENTED DENORMALIZED EVENTUALLY CONSISTENT INDEXABLE BASE 16 The main concerns of a domain model are mainly about business logic validation and enforcing business rules and domain invariants. So we might decide that the most important attributes for our domain model and its repository are like to be normalized, transactional or consistent. As for the Read model, concerns are different It only cares about presenting data to the user so, in this case, other attributes could be more convenient, like to be denormalized (so data access is faster), indexable or eventual consistent (it’s not a big deal if data is stale some times) These attributes may or may not the best choice for any given system, but the point is that different trade-offs apply and now we can make totally different choices for each model.
  • 17. ★ the CQRSdiet ★ Event Handler COMMANDSQUERIES EVENTS UPDATES Project List Project Details DOMAIN MODEL READ MODEL TaskProject 17 So now we have two different models: A domain model, responsible of commands, and a read model, responsible of queries. Each of them with totally different attributes. But there’s something missing here, as you’ve probably noticed. In order for this to work at all, we need to keep both models consistent and, preferably, uncoupled as well. So, in order to get that, the piece that we need to add to the system is some kind of syncing mechanism that listens to the changes happening in the domain model and updates the read model accordingly. This agent, ensures consistency and, since its based on events, it keeps both models unaware of each other.
  • 18. Simpleexampleusing justthetoolsinthebox (i.e.Rails) 18 To better illustrate this, we’re gonna see a very simple implementation using anything else but Rails.
  • 19. ★ the CQRSdiet ★ class Project < ActiveRecord::Base # t.string :name # t.text :notes # t.datetime :deadline # t.boolean :active has_many :tasks scope :active, where(:active => true) validates_presence_of :name def status active? ? "Active" : "Inactive" end end INITIALMODEL 19 We have this initial model. No CQRS here, yet, ok? It’s an ActiveRecord model which has four attributes: a name, some notes, a deadline, and a flag that indicates whether the project is active or not A project has many tasks It seems that it needs to be filtered by the active flag The presence of a name is necessary for a Project to be valid And finally, we’ve got this handy method that returns a user-friendly string denoting the status of the project. How about our controller?…
  • 20. ★ the CQRSdiet ★ CONTROLLER class ProjectsController < ApplicationController def index @projects = end def create project = Project.create!(params[:project]) redirect_to(project, :notice => 'Project created.') end def destroy project = Project.find(params[:id]) project.destroy redirect_to(projects_url) end end ProjectReport.activeProject.active QUERY COMMAND COMMAND 20 …Nothing unexpected here either. We’ve got a nice, skinny controller which deals with two commands (create and destroy) and one query (the index action). All three actions are using the same Project model we saw in the previous slide. And that’s what we’re going to change. Queries and Commands should use different models so, for our index action we’re replacing Project with a new read model we call ProjectReport. Now let’s see how ProjectReport looks like…
  • 21. ★ the CQRSdiet ★ class ProjectReport < ActiveRecord::Base # t.string :name # t.string :status # => “Active” or “Inactive” # t.integer :project_id scope :active, where(:status => "Active") end READMODEL 21 First thing to notice here is that we are using ActiveRecord, but we could be using anything else. From a more lightweight ORM like Sequel, to any NoSQL database, like Redis, CouchDB or MongoDB. Whatever we think that will work better for our read model which only purpose is, I remind you: populating views with data as fast and as conveniently as possible. So, it is just for the sake of the example that we stick to ActiveRecord here. What have here? First thing are the attributes. We’ve got only those that are shown to the user, like the name, the status, which it’s handy that it stores the string to be shown to the user (not a boolean flag that we’d need to transform later) And a reference to the Project in the domain model, that will help us keep track of its changes. Besides attributes, we’ve moved here the “active” scope. Filtering is part the read model so this scope belongs to here. Let’s go back to our domain model now...
  • 22. ★ the CQRSdiet ★ class Project < ActiveRecord::Base DOMAINMODEL scope :active, where(:active => true) validates_presence_of :name def status active? ? "Active" : "Inactive" end has_many :tasks end # t.string :name # t.datetime :deadline # t.boolean :active # t.text :notes 22 Our initial model used to look like this. But now, we have a Read Model which frees this one from a couple of its responsibilities Like filtering, this scope is no longer being used (controllers use the one in the read model) so, let’s get rid of it…
  • 23. ★ the CQRSdiet ★ class Project < ActiveRecord::Base validates_presence_of :name def status active? ? "Active" : "Inactive" end has_many :tasks end DOMAINMODEL # t.string :name # t.datetime :deadline # t.boolean :active # t.text :notes 23 What else? The status. Now it is stored in the read model and our domain logic doesn’t need it for anything. So let’s remove it too…
  • 24. ★ the CQRSdiet ★ class Project < ActiveRecord::Base validates_presence_of :name # t.string :name has_many :tasks end # t.datetime :deadline # t.boolean :active DOMAINMODEL # t.text :notes 24 Nice! Removing code is one of the great joys of being a programmer, don’t you think? So, we end up with this two models…
  • 25. ★ the CQRSdiet ★ class ProjectReport < ActiveRecord::Base # t.string :name # t.string :status # t.integer :project_id scope :active, where(:status => "active") end DOMAINMODELREADMODEL class Project < ActiveRecord::Base # t.string :name # t.string :status # t.datetime :deadline # t.boolean :active has_many :tasks validates_presence_of :name end 25 …that we need to keep consistent. We need something that listens to the Domain Model and updates the Read Model accordingly. For that, one of the simplest tool we can use, right out of Rails box, is…
  • 26. ★ the CQRSdiet ★ OBSERVER class ProjectObserver < ActiveRecord::Observer def after_save(project) report = ProjectReport.find_or_create_by_project_id(project.id) report.update_attributes! :name => project.name, :status => project.active ? "Active" : "Inactive" end def after_destroy(project) ProjectReport.destroy_all(:project_id => project.id) end end 26 …An observer. By definition, observers listen to changes in other objects and do something about them. So they seem exactly what we need Every time a Project is saved in the Domain, we will find or create a related ProjectReport in the Read Repository and update the name and the status accordingly. Note that for the status, we made here the transformation from the active flag in the domain model to the string we store in the read model. Besides that when a Project is removed from the domain repository, we want any related ProjectReport to be removed from the read model as well. And that’s all about the observer…
  • 27. ★ the CQRSdiet ★ class ProjectReport < ActiveRecord::Base # t.string :name # t.string :status # t.integer :project_id scope :active, where(:status => "active") end DOMAINMODELREADMODEL class Project < ActiveRecord::Base # t.string :name # t.string :status # t.datetime :deadline # t.boolean :active has_many :tasks validates_presence_of :name end class ProjectObserver < ActiveRecord::Observer def after_save(project) report = ProjectReport.find_or_create_by_project_id(project.id) report.update_attributes! :name => project.name, :status => project.active ? "Active" : "Inactive" end def after_destroy(project) ProjectReport.destroy_all(:project_id => project.id) end end OBSERVER 27 We are quite done with this example. This slide summarizes what we ended up with. You know, the problem with examples is they have to be simple to avoid distraction, but, just because of that, often they don’t justify the technique you’re showing. Our initial model had no “overweight” problem to begin with. But, still, the point is that the example helps you understand the pattern. If that’s not the case just raise your hand and ask.
  • 28. ★ the CQRSdiet ★ Similar,real-worldexample* ★ MongoDBfortheReadModel (Presenters Silos) ★ AsynchronousObserverswith RabbitMQ(Silovators) DenormalizingYourRailsApplication by Daniel Lucraft & Matt Wynne ScottishRubyConference2010 * 28 Ok, before moving on to something else, I just want to point out an interesting real-world example of this approach It was presented by Daniel Lucraft and Matt Wynne in the latest Scottish Ruby Conf and there’s a video available in the internet. In the talk they describe the architecture of songkick (a social website about music and concerts) which is very similar to what we’ve just seen / They use MongoDB for the Read Model, using Presenters and what they call, “Silos”, and, the other interesting thing is that their observers work asynchronously (which is very typical) using RabbitMQ queues. So, this is an interesting resource if you want to dig deeper into this basic approach to CQRS.
  • 29. ★ the CQRSdiet ★ EVENTS 29 So, let’s move on to the next big topic of the talk: Events. And I don’t mean last night’s event which was awesome. I mean…
  • 30. ★ the CQRSdiet ★ EVENTSDOMAIN 30 …Domain Events.
  • 31. ★ the CQRSdiet ★ READREPOSITORYDOMAIN EVENTS EVENT HANDLERS 31 Let’s say that we are sold to CQRS and now we have a whole system that segregates reads and writes. Even in colors! We’ll soon realize that domain events have become a critical part our system.
  • 32. ★ the CQRSdiet ★ Whyeventsarekey? ★ Eventskeepmodels consistent ★ Eventskeepmodels loosely coupled ★ Eventskeeptrackofallchanges 32 Why? Well they keep our data models consistent, we already know that. But there’s more. They also keep our data models loosely coupled. This is very important. We could try to keep our models consistent in different ways like, for instance, by asking the domain model about its state. However, doing that we would be adding coupling into the system, making it brittle (you know, changes in one part of the system would cause other parts of the system to break). So instead of that, we use events. Events don’t expose the state of the domain but only the *changes* in the domain. That maintains the domain model isolated from the outside world, which is nice to make changes without affecting other parts of the system. And there’s one more important thing about events They keep track of absolutely any change that happens in the domain. And that could make events no less than...
  • 33. ★ the CQRSdiet ★ Source Truth The of 33 …The Source of Truth! And we would have new ways to add value to our system. Let’s see them.
  • 34. ★ the CQRSdiet ★ READREPOSITORYDOMAIN EVENTS EVENT HANDLERS EVENTSTORE 34 Ok, there we are. The first further step we can take is to store every event that the domain publishes. If domain events are going to be our source of truth, we definitely need to store them. This will give us some immediate value…
  • 35. ★ the CQRSdiet ★ Eventstore ★ Comprehensiveauditlog ★ Abilitytoreplayevents 35 First of all we’ve got the most comprehensive audit log, which for some domains (like Accounting) is a must, and for the rest, it can provide a great business value. This is not the same as any other infrastructure log, this events has meaning to the domain, to the business. Think about diagnosing problems or giving customer support. The value of such a log could be huge. And with an event store, we got this value virtually for free. Besides that we have now the ability to replay events. Let’s say that we find a bug in one of our event handlers that makes one of our views to show incorrect data. When we fix the bug, we also need to fix the corrupt data in the read repository. Normally you would do it kind of manually: you’d run a script over the read repository that fixes the data. But now, thanks to our shiny event store, we could do something else, we could just replay all the related events over the, now fixed, event handler so that the correct dataset will be generated. Nice, but there’s more…
  • 36. ★ the CQRSdiet ★ DomainPersistence 36 …and it has to do with Domain Persistence. We want our events to be our source of truth. So let’s say that we implement in our Domain objects the ability to apply events to them. With that, we have now the capacity to re- construct any Domain object up to its latest state just by applying its event stream to it. And, if we have this capacity, we no longer need any persistence solution for our domain other than…
  • 37. ★ the CQRSdiet ★ DomainPersistence EventStore 37 The event store.
  • 38. ★ the CQRSdiet ★ READREPOSITORYDOMAIN EVENTS EVENT HANDLERS EVENTSTORE 38 That’s it. Every time we need a certain domain object to process a command the Domain Repository will get the stream of events related to that object from the Event Store. Then, it will instantiate a new object and it’ll apply the events to it. That way we get the object in its current state, ready to process the next command. We won’t need any other persistence mechanism. We won’t ever “save” domain objects, we will just *publish* events. We’re gonna see an example of this. But first let me just point out that all this approach around events has a name which is…
  • 39. ★ the CQRSdiet ★ EVENTSOURCING 39 “Event Sourcing”, and it is a design pattern in its own right that happens to work very well with CQRS. So now that we have a name, let’s see an implementation example of a CQRS system with Event Sourcing
  • 40. ★ the CQRSdiet ★ github.com/cavalle/banksimplistic 40 The example is extracted from a sandbox application that I use to experiment with different implementations of these ideas. It is called BankSimplistic, it’s about banking (not surprisingly), and you can find the code in github if you want to check it out.
  • 41. ★ the CQRSdiet ★ FEATURE feature "Deposit cash", %q{ In order to have money to lend to other clients As a banker I want clients to deposit cash into their accounts } 41 In our bank we want our clients to be able to deposit cash into their accounts, so that we have money to invest in subprime mortgages, right? So, “Deposit cash” is the functionality we’re looking into in this example.
  • 42. ★ the CQRSdiet ★ class DepositsController < ApplicationController # POST /accounts/23/deposits def create account = Account.find(params[:account_id]) account.deposit(params[:deposit][:amount]) redirect_to account end end CONTROLLER 42 We start with the controller. Let’s say that we receive a POST request to make a deposit into a given account. First thing we do is to fetch the Account from the Domain Repository, finding it by id. Then we invoke its deposit method passing it through the amount of money. Finally we just redirect the user to the Account page. And that’s all. Two things to note here: 1st, the deposit method sounds like a command (it is quite clear what we’re doing, we’re not updating attributes or anything like that). And the 2nd thing is that we’re not saving the account. And this is not because the save call is inside the deposit method as we’re going to see right now. It is because we don’t have to save anything. So let’s see the model
  • 43. ★ the CQRSdiet ★ DOMAINMODEL class Account include AggregateRoot def initialize @balance = 0 end def deposit(amount) # Do some validation new_balance = @balance + amount publish :deposit_made, :amount => amount, :new_balance => new_balance, :account_uid => uid end 43 Ok, the Account model includes a module called AggregateRoot. Don’t worry very much about this name. All you need to know is that it provides the necessary infrastructure logic. So our model has an initialize method that sets the balance of the account to zero. Makes sense that new accounts has no money, of course. Then we have our deposit method. The first thing you put in a method like this is some kind of validation logic related to the command being processed. You would check here things like deposit limits or whether the account is open, for instance. Next, if validation is ok, the new balance is calculated, just by adding the amount to the current balance. That’s our domain logic. And, finally, the method publishes an event called “deposit_made” that includes the amount of the deposit, the new balance, and the id of the account where it has been made. And that’s all. The interesting thing here is that we’re not updating the balance of the account inside this method. We’re just calculating the new balance and then publishing the event.
  • 44. ★ the CQRSdiet ★ DOMAINMODEL class Account include AggregateRoot def initialize @balance = 0 end def deposit(amount) # Do some validation new_balance = @balance + amount publish :deposit_made, :amount => amount, :new_balance => new_balance, :account_uid => uid end def on_deposit_made(event) @balance = event.data[:new_balance] end 44 To update the balance of the account we have this “on_deposit_made” method that will receive the event that the command generates and will update the balance. And, why is that? you may ask. Well, good question. We need this “on” methods because, as I mentioned earlier, we need to be able to reconstruct a domain object just by applying its events. That’s why any logic that updates the state of the object needs to be inside one of these event handling methods. That’s what these methods are: internal event handlers. To understand this better, let’s take a look one level down at the infrastructure logic. To begin with, what does the `publish` method do?
  • 45. ★ the CQRSdiet ★ THEMAGIC def publish(name, attributes) event = Event.new(:name => name, :data => attributes) do_apply event event.aggregate_uid = uid published_events << event end def do_apply(event) method_name = "on_#{event.name}" method(method_name).call(event) end 45 It starts by creating a new event with its name and data. Good Then it calls a method called “do_apply” and what that method does is precisely calling the internal event handler we saw earlier. Makes sense? Then the id of the object is added to the event data and finally the event is added to a published_events collection from where it will be finally published to the external world and, also, saved to the event store.
  • 46. ★ the CQRSdiet ★ THEMAGIC def build_from(events) object = self.new events.each do |event| object.send :do_apply, event end object end def find(klass, uid) events = Event.find(:aggregate_uid => uid) klass.build_from(events) end 46 Some other magic you might find interesting is the find method we used from the controller to get the Account object It first gets all the events related to the requested object, and then it calls the build_from method in the Account class, passing it through the event stream. That build_from method will initialize a new instance of the class and then it will apply each event to the object using the same “do_apply” method we saw before. And then the object will be ready for the next command. And that’s pretty much all about this example
  • 47. ★ the CQRSdiet ★ DOMAINMODEL class Account include AggregateRoot def initialize @balance = 0 end def deposit(amount) # Do some validation new_balance = @balance + amount publish :deposit_made, :amount => amount, :new_balance => new_balance, :account_uid => uid end def on_deposit_made(event) @balance = event.data[:new_balance] end 47 I’ve skipped the part of updating the read model since it would be quite similar to what I showed you in the first example. Summarizing: In this example the command method does three things: first it validates that the command is ok to be processed (no domain invariant is broken, and so on and so forth). Second it applies the necessary business logic required by the command. And third it publishes an event. But it doesn’t change the state of the object. This is done in an internal handler for that event just published. That way, the domain repository will be able to restore domain objects just from their events.
  • 48. ★ the CQRSdiet ★ TIMEFOR AWEIGHT CHECK 48 At the beginning of the talk we decided to put our models on a diet. Now I think it’s time for a weight check
  • 49. ★ the CQRSdiet ★ ★Validation ★Domain Logic methods&callbacks ★Structureassociations ★Filtering scopes ★Presentation helpermethods ★Persistence 49 We started with models which had all these responsibilities. The first we did was creating a separated read model. That freed our domain model from Presentation and Filtering. We can also say that those associations used only for filtering could be removed from the domain as well. Then, in the second part of the talk, we saw the value of using an Event Store as the source of truth of the system and decided to use it also as the only persistence mechanism of the domain. So our models no longer need to worry about persistence. We end up with a nice, thin Domain model that now, can focus in its most important responsibilities: validation and domain logic. It’ll be more about behaviours rather than structure. By the way, talking about responsibility…
  • 50. ★ the CQRSdiet ★ Therewillbe(some) dragons ★ MorecomplexarchitecturethanRails’MVC ★ More“movingparts”intheinfrastructure ★ Distributedandasynchronousworldischaotic ★ Problemsthatusedtobetrivial nolonger are 50 It wouldn’t be very responsible of me to let you go from these talk thinking we’ve got a silver bullet. I’m sorry to say there is no such a thing as a silver bullet. Everything has trade-offs. So, I should mention that with CQRS the simplest case, say the “Hello world” app, is more complex than with Rails MVC. We have two models instead of one and we have to coordinate them. We saw it in the first example: we finished with more lines of code than we started. We’ll also have more “moving parts” in our infrastructure. You know? It is very nice to only have to worry about one database and one web server. That’s happiness! But with CQRS it is easy to end up with several different databases, one messaging queue, not to mention caching or text indexing. Be careful with that. More “moving parts” means more things that can stop working so more things to monitor, and it becomes more difficult to diagnose problems. Also as soon as you decide to handle your events asynchronously, you will enter to the distributed world which could be kind of chaotic: messages arrive out-of-order or duplicated, everything is asynchronous, consistency in the read model is eventual so you have to deal with this in the User Interface, etc. This is a new world and it requires you to change your mindset, which is not always easy. Related to this you’ll find that some problems that used to be trivial, no longer are. For instance validating the uniqueness of a value might not be as simple as it used to. Again, you have to change your mindset, think differently, constraints have changed.
  • 51. ★ the CQRSdiet ★ Youneeda goodreason ★ Complexdomainlogic ★ Complexexistent infrastructure ★ Complexscalabilityneeds 51 So, since there will be dragons you need a good reason to get into CQRS. I’d mention three: A complex domain logic (separating responsibilities makes things simpler), an existing complex infrastructure (CQRS would give you a framework to organize it better) or complex scalability needs (with CQRS, you are partitioning your system at application level and that makes it quite scalable). In all, you need to have...
  • 52. ★ the CQRSdiet ★ ComplexProblem 52 a complex problem to be solved for CQRS to pay off
  • 53. ★ the CQRSdiet ★ FINAL THOUGHTS 53 So, we’re almost done. Some final thoughts.
  • 54. ★ the CQRSdiet ★ Separating reads& writesisnotnew ★ Databases ★ HTTP ★ Infrastructurelevel ★ Performance orScalability 54 The idea of separating reads & writes in your system isn’t new at all. We’ve been doing that with databases for ages (you don’t need to look at NoSQL databases like CouchDB where this is pretty explicit, you just need to look at the classic master-slave schema with mysql, where one database is only used for reading) One of the key points of HTTP (and the REST philosophy) is that it segregates reads and writes with its four verbs, so that any proxy can take advantage of knowing whether a request is a GET or not, and do interesting things about it (like caching) However, the segregation in these cases is made at infrastructure level and because of performance or scalability reasons.
  • 55. ★ the CQRSdiet ★ Separatingby functionisnotnew ★ Bigsystems(e.g.Amazon,Ebay)partition its functionalityat applicationlevel ★ Butagainthe motivation isscalability 55 Separating by function isn’t new either Big systems like Amazon’s or Ebay’s partition its functionality at application level. Although there’s only one user interface, they have separated systems for Orders, Billing or Shipping. That way it is easier for them to scale the system But again, that’s the motivation, scalability
  • 56. ★ the CQRSdiet ★ Separatereads&writes at applicationlevel 56 In my opinion, what makes CQRS interesting is that it takes the idea of separating reads & writes and applies it at application level
  • 57. ★ the CQRSdiet ★ Benefits in terms of maintainbility 57 And because of that you get benefits in terms of maintainability. It makes easier for you to write a beautiful, expressive domain model
  • 58. ★ the CQRSdiet ★ Performanceandscalability asa side effect VERYNICE Benefits in terms of maintainbility 58 And yes, as a nice side effect, your system will perform and scale quite well
  • 59. Luismi Cavallé @cavalle THANKS! ★ the CQRS diet ★ 59
  • 60. ★ the CQRSdiet ★ Resources ★ github.com/cavalle/banksimplistic ★ cqrsinfo.com ★ groups.google.com/group/dddcqrs 60
  • 61. Thisslideisintentionally leftblank 61

×