{"id":1580,"date":"2009-12-21T12:07:13","date_gmt":"2009-12-21T11:07:13","guid":{"rendered":"http:\/\/blog.jteam.nl\/?p=1580"},"modified":"2009-12-21T12:07:13","modified_gmt":"2009-12-21T11:07:13","slug":"rethinking-architecture-with-cqrs","status":"publish","type":"post","link":"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/","title":{"rendered":"Rethinking architecture with CQRS"},"content":{"rendered":"<p><a href=\"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/question_and_answer.png\"><img loading=\"lazy\" decoding=\"async\" style=\"margin: 5px 10px 0px 0px;border-width: 0px\" title=\"question_and_answer\" src=\"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/question_and_answer_thumb.png\" border=\"0\" alt=\"question_and_answer\" width=\"64\" height=\"64\" align=\"left\" \/><\/a> Many applications use some form of persistent storage to store its state. However, important information about this state is lost: why is the state as it currently is. Furthermore, a single model is used to store information that is retrieved for many different purposes, often resulting in extremely complex and bog-slow SQL queries.<\/p>\n<p>Command Query Responsibility Segregation (CQRS) is an architectural style that makes a clear distinction between commands that change the application state and queries that expose the application state.<\/p>\n<p><!--more--><\/p>\n<h2>Background<\/h2>\n<p>My interest in CQRS was triggered when I saw Greg Young explain \u201c<a href=\"http:\/\/www.infoq.com\/interviews\/greg-young-ddd\" target=\"_blank\" rel=\"noopener\">State Transitions in Domain-Driven Design<\/a>\u201d on InfoQ. In this interview, Greg explains how he sees that application would benefit from using separate models for state validation and transition on one side and maintaining a view on the current state on the other.<\/p>\n<p>One of the problems that Greg describes is the fact that a single model is often used for different purposes. It is nicely illustrated by the SQL query below. It shows how the model chosen in the application is not suited for the purpose of providing certain information (messages between users, in this case).<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">queryBuilder.append(\n  &quot;m.*, &quot; +\n  &quot;m.origin_participant_id as message_origin_participant_id, &quot; +\n  &quot;po.first_name as message_origin_participant_first_name, &quot; +\n  &quot;po.avatar as message_origin_participant_avatar, &quot; +\n  &quot;po_ua.username as message_origin_participant_username, &quot; +\n  &quot;CASE WHEN !isnull(po.fieldworker_project_id) THEN 'fieldworker' WHEN !isnull(po_ap.project_id) THEN 'fundraiser' ELSE 'player' END AS message_origin_participant_type, &quot; +\n  &quot;po.city as message_origin_participant_city, &quot; +\n  &quot;c.name as message_origin_participant_country, &quot; +\n  &quot;pd.first_name as message_destination_participant_first_name, &quot; +\n  &quot;pd.avatar as message_destination_participant_avatar, &quot; +\n  &quot;pd_ua.username as message_destination_participant_username, &quot; +\n  &quot;CASE WHEN !isnull(pd.fieldworker_project_id) THEN 'fieldworker' WHEN !isnull(pd_ap.project_id) THEN 'fundraiser' ELSE 'player' END AS message_destination_participant_type, &quot; +\n  &quot;m.destination_participant_id as message_destination_participant_id &quot; +\n  &quot;from internal_message m  &quot; +\n  &quot;left join player po on m.origin_participant_id = po.id &quot; +\n  &quot;left join (select player_id, project_id from ambassador_project where enabled = true group by player_id) po_ap on po_ap.player_id =  po.id &quot; +\n  &quot;left join user_account po_ua on po.user_account_id = po_ua.id &quot; +\n  &quot;left join country c on po.country_id = c.id &quot; +\n  &quot;left join player pd on m.destination_participant_id = pd.id &quot; +\n  &quot;left join (select player_id, project_id from ambassador_project where enabled = true group by player_id) pd_ap on pd_ap.player_id =  pd.id &quot; +\n  &quot;inner join user_account pd_ua on pd.user_account_id = pd_ua.id &quot; +\n  &quot;where m.destination_participant_id = ? &quot;\n);\n\n<\/pre>\n<p>Wouldn\u2019t it be a lot nice to have a query such as the one below instead?<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">&lt;\/pre&gt;\nSELECT * FROM messages WHERE receiving_participant = ?\n&lt;pre&gt;<\/pre>\n<p>Achieving such queries is very easy, but doing so without making your model impossible to use for maintaining state integrity and guarding invariants is a lot harder. Well, unless you apply CQRS.<\/p>\n<h2>Architecture<\/h2>\n<p>The diagram below shows an overview of a typical CQRS architecture.<\/p>\n<p><a href=\"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/cqrs_architecture.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"border-width: 0px;margin-left: auto;margin-right: auto\" title=\"cqrs_architecture\" src=\"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/cqrs_architecture_thumb.jpg\" border=\"0\" alt=\"cqrs_architecture\" width=\"404\" height=\"281\" \/><\/a><\/p>\n<p>When a command comes in, it will load an <a href=\"http:\/\/dddstepbystep.com\/wikis\/ddd\/aggregate.aspx\" target=\"_blank\" rel=\"noopener\">aggregate<\/a> from the <a href=\"http:\/\/dddstepbystep.com\/wikis\/ddd\/repository.aspx\" target=\"_blank\" rel=\"noopener\">repository<\/a> and execute certain operations on it. As a result of these operations, the aggregate produces events, which are picked up for storage by the repository and for dispatching by the event bus. The event bus will dispatch each event to all (interested) event handlers. Some of these event handlers will perform actions on other (related) aggregates, some others will update the database tables they manage.<\/p>\n<p>Having handlers update the data in the database means that your tables do not have to be normalized anymore. Instead, CQRS allows you to optimize your tables for the way you want to query them. This makes the data layer processing your queries really simple, and maintainable.<\/p>\n<p>Furthermore, since all state changes are initiated by events, these events become a reliable source for an audit trail. By storing these events, you have an audit trail that you can use to replay the entire application history. Instead of just seeing \u201ccurrent application state\u201d only, you can see all the changes that have led to the current state, providing valuable information trying to find bugs or deal with customer complaints.<\/p>\n<h2>Benefits<\/h2>\n<p><strong>Matches the natural language used<\/strong><\/p>\n<p>At first glance, such an architecture might look very complex and over-engineered. However, after taking the first implementation steps, it felt pretty natural. In fact, when you explain the behavior of an application, you use a style that fits CQRS quite nicely: \u201cwhen an order is approved, we send a confirmation email to the customer\u201d.<\/p>\n<p><strong>Extensibility<\/strong><\/p>\n<p>Imagine an order management application. With CQRS, you could have an \u201cOrderApproved\u201d event, which is caught by a database updating event handler as well as one that sends an email to the customer with order information. If you later on decide to store the order information in an accounting tool as well, all it takes is adding an event handler that does the accounting integration. And since you can reload historic events as well, you can even reconstruct historic information to put in the accounting table.<\/p>\n<p><strong>Reliable audit trail<\/strong><\/p>\n<p>As I said above [see section Architecture], the model that is used to process command will generate events. These events are the sole source of state changes for the domain classes. If you want to load an aggregate from persistent storage, it will actually load all the past events of that aggregate. This process is called Event Sourcing. When events become too numerous, you can combine a number of historic events into a single snapshot event. The events that have been combined can then be archived for auditing purposes.<\/p>\n<p>Event Sourcing turns your event storage into an extremely reliable audit trail. The events do no only show what happened and when, but they will also reveal the intention a user had. Not only is it an audit trail that will never move out-of-sync with your application, you can use the audit trail to actually replay all actions in the application. Events are not just a notification of state change, they are also the source of state change.<\/p>\n<p><strong>Transparent distributed processing<\/strong><\/p>\n<p>When using events as the trigger for state changes, you can easily distribute your application over multiple processing units (servers, JVM\u2019s, whatever). Events can easily be serialized and sent from one server to another over JMS, via the file system or even email. The Spring Integration framework \u2013a messaging framework \u2013 fits nicely with the event processing concept.<\/p>\n<p>You could even let certain types of aggregates live on dedicated machines. This allows you to choose different SLA\u2019s for different parts of you application, such as giving order creation a higher priority than sales reporting.<\/p>\n<p><strong>Performance and Scalability<\/strong><\/p>\n<p>Since event handling is done asynchronously, a user does not have to wait for all changes to be applied. Especially when integrating with external systems, this can improve liveness of an application significantly.<\/p>\n<p>Another aspect of CQRS is that it heavily builds upon BASE (<strong>B<\/strong>asic <strong>A<\/strong>vailability, <strong>S<\/strong>oft-state, <strong>E<\/strong>ventual consistency) transactions. Although the \u201ceventual\u201d part scares a lot of customers and developers, the price of distributed ACID transactions is one that customer typically resent. In most cases \u201ceventual\u201d is just a matter of several milliseconds. But you\u2019re free to make that seconds, minutes or even hours if your SLA permits it (think of management reports that are only read once a week or month).<\/p>\n<p><strong>Asynchronous analysis<\/strong><\/p>\n<p>When you build a CQRS style application, all activity in your application is processed using events. This makes it easy to catch these events and monitor them for inconsistent behavior. Event analysis tools like <a href=\"http:\/\/esper.codehaus.org\/\" target=\"_blank\" rel=\"noopener\">Esper<\/a> allow you to monitor event streams for patterns that indicate possible fraud.<\/p>\n<p>For example, when you detect that a sudden large number of users has an increased amount of failed login attempts, you could decide to block these accounts for a small period of time. In one of our projects, we use <a href=\"http:\/\/www.rsa.com\/node.aspx?id=3018\" target=\"_blank\" rel=\"noopener\">RSA Adaptive Authentication<\/a> to increase the level of authentication required when someone tries to login on an account that might be the subject of dictionary attacks.<\/p>\n<h2>cqrs4j \u2013 an open source CQRS Framework<\/h2>\n<p><a href=\"http:\/\/code.google.com\/p\/cqrs4j\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" style=\"margin-left: 0px;margin-right: 0px;border-width: 0px\" title=\"cqrs4j logo\" src=\"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/logo_large.png\" border=\"0\" alt=\"cqrs4j logo\" width=\"100\" height=\"100\" align=\"left\" \/><\/a> A few days ago, I published the source code of a CQRS framework \u2013 <a href=\"http:\/\/code.google.com\/p\/cqrs4j\" target=\"_blank\" rel=\"noopener\">cqrs4j<\/a> \u2013 on Google code. This framework aims at reducing the amount of \u201cplumbing\u201d and boilerplate code to a minimum. It also provides integration support for Spring Integration and transactional event processing.<\/p>\n<p>You can find project information, source code and documentation at <a href=\"http:\/\/code.google.com\/p\/cqrs4j\/\">http:\/\/code.google.com\/p\/cqrs4j\/<\/a>.<\/p>\n<p>[Update: cqrs4j has been renamed to Axon Framework. You can find more information on www.axonframework.org].<\/p>\n<h2>Upcoming presentations and events<\/h2>\n<p>JTeam organizes monthly Tech Meetings, where interesting technologies are discussed. The January 2010 edition will contain a presentation about CQRS. Registration is required, but free of charge. The tech meeting is held on January 7th in our office in Amsterdam [<a href=\"http:\/\/www.jteam.nl\/contact.html\" target=\"_blank\" rel=\"noopener\">directions<\/a>] and starts at 16:00. It includes dinner.\u00a0 Send us an <a href=\"mailto:techmeeting@jteam.nl?subject=I%20want%20to%20join%20the%203rd%20Dec%20Tech%20meet%20%40%20JTeam&amp;body=Name%3A%0AOrganization%3A%0Aemail%3A%0ATel.%23%0A\" target=\"_blank\" rel=\"noopener\">email<\/a> if you like to attend.<\/p>\n<p>The DDDnl user group organizes a presentation and discussion session on March 9th, starting at 18:00. It is also held at the JTeam office in Amsterdam [<a href=\"http:\/\/www.jteam.nl\/contact.html\" target=\"_blank\" rel=\"noopener\">directions<\/a>]. Attendance requires <a href=\"http:\/\/www.dddnl.org\/user\/register\" target=\"_blank\" rel=\"noopener\">registration<\/a> with the DDDnl user group, which is completely free of charge. Visit <a href=\"http:\/\/www.dddnl.org\" target=\"_blank\" rel=\"noopener\">dddnl.org<\/a> for more information.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Many applications use some form of persistent storage to store its state. However, important information about this state is lost: why is the state as it currently is. Furthermore, a single model is used to store information that is retrieved for many different purposes, often resulting in extremely complex and bog-slow SQL queries. Command Query [&hellip;]<\/p>\n","protected":false},"author":14,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"categories":[10],"tags":[32,140,46,141],"class_list":["post-1580","post","type-post","status-publish","format-standard","hentry","category-development","tag-application-design","tag-architecture","tag-cqrs","tag-domain-driven-design"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Rethinking architecture with CQRS - Trifork Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Rethinking architecture with CQRS - Trifork Blog\" \/>\n<meta property=\"og:description\" content=\"Many applications use some form of persistent storage to store its state. However, important information about this state is lost: why is the state as it currently is. Furthermore, a single model is used to store information that is retrieved for many different purposes, often resulting in extremely complex and bog-slow SQL queries. Command Query [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/\" \/>\n<meta property=\"og:site_name\" content=\"Trifork Blog\" \/>\n<meta property=\"article:published_time\" content=\"2009-12-21T11:07:13+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/question_and_answer_thumb.png\" \/>\n<meta name=\"author\" content=\"Allard Buijze\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Allard Buijze\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/\",\"url\":\"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/\",\"name\":\"Rethinking architecture with CQRS - Trifork Blog\",\"isPartOf\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/#primaryimage\"},\"thumbnailUrl\":\"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/question_and_answer_thumb.png\",\"datePublished\":\"2009-12-21T11:07:13+00:00\",\"author\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/a4e232a11dc57a2c4c581956ce6fde63\"},\"breadcrumb\":{\"@id\":\"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/#primaryimage\",\"url\":\"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/question_and_answer_thumb.png\",\"contentUrl\":\"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/question_and_answer_thumb.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/trifork.nl\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Rethinking architecture with CQRS\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/trifork.nl\/blog\/#website\",\"url\":\"https:\/\/trifork.nl\/blog\/\",\"name\":\"Trifork Blog\",\"description\":\"Keep updated on the technical solutions Trifork is working on!\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/trifork.nl\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/a4e232a11dc57a2c4c581956ce6fde63\",\"name\":\"Allard Buijze\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/3e7130b2465615e105b1addd400a3f06?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/3e7130b2465615e105b1addd400a3f06?s=96&d=mm&r=g\",\"caption\":\"Allard Buijze\"},\"description\":\"Allard (@allardbz) is CTO of Trifork Amsterdam and founder of Axon Framework. He is a trainer and speaker at conferences on topics related to scalable architecture and domain driven design. He strongly believes that good craftsmanship can only be achieved through continuous and intensive exchange of experience with others. The last years, he has been investigating and applying CQRS to a number of projects. As a result, he created the Axon Framework, an open source Java framework that helps developers create scalable and extensible applications. Axon has a growing community and has already been successfully introduced in several high-profile projects around the world.\",\"url\":\"https:\/\/trifork.nl\/blog\/author\/allard\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Rethinking architecture with CQRS - Trifork Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/","og_locale":"en_US","og_type":"article","og_title":"Rethinking architecture with CQRS - Trifork Blog","og_description":"Many applications use some form of persistent storage to store its state. However, important information about this state is lost: why is the state as it currently is. Furthermore, a single model is used to store information that is retrieved for many different purposes, often resulting in extremely complex and bog-slow SQL queries. Command Query [&hellip;]","og_url":"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/","og_site_name":"Trifork Blog","article_published_time":"2009-12-21T11:07:13+00:00","og_image":[{"url":"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/question_and_answer_thumb.png","type":"","width":"","height":""}],"author":"Allard Buijze","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Allard Buijze","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/","url":"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/","name":"Rethinking architecture with CQRS - Trifork Blog","isPartOf":{"@id":"https:\/\/trifork.nl\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/#primaryimage"},"image":{"@id":"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/#primaryimage"},"thumbnailUrl":"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/question_and_answer_thumb.png","datePublished":"2009-12-21T11:07:13+00:00","author":{"@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/a4e232a11dc57a2c4c581956ce6fde63"},"breadcrumb":{"@id":"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/#primaryimage","url":"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/question_and_answer_thumb.png","contentUrl":"http:\/\/blog.jteam.nl\/wp-content\/uploads\/2009\/12\/question_and_answer_thumb.png"},{"@type":"BreadcrumbList","@id":"https:\/\/trifork.nl\/blog\/rethinking-architecture-with-cqrs\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/trifork.nl\/blog\/"},{"@type":"ListItem","position":2,"name":"Rethinking architecture with CQRS"}]},{"@type":"WebSite","@id":"https:\/\/trifork.nl\/blog\/#website","url":"https:\/\/trifork.nl\/blog\/","name":"Trifork Blog","description":"Keep updated on the technical solutions Trifork is working on!","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/trifork.nl\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/a4e232a11dc57a2c4c581956ce6fde63","name":"Allard Buijze","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/3e7130b2465615e105b1addd400a3f06?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/3e7130b2465615e105b1addd400a3f06?s=96&d=mm&r=g","caption":"Allard Buijze"},"description":"Allard (@allardbz) is CTO of Trifork Amsterdam and founder of Axon Framework. He is a trainer and speaker at conferences on topics related to scalable architecture and domain driven design. He strongly believes that good craftsmanship can only be achieved through continuous and intensive exchange of experience with others. The last years, he has been investigating and applying CQRS to a number of projects. As a result, he created the Axon Framework, an open source Java framework that helps developers create scalable and extensible applications. Axon has a growing community and has already been successfully introduced in several high-profile projects around the world.","url":"https:\/\/trifork.nl\/blog\/author\/allard\/"}]}},"_links":{"self":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/1580","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/users\/14"}],"replies":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/comments?post=1580"}],"version-history":[{"count":0,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/1580\/revisions"}],"wp:attachment":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/media?parent=1580"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/categories?post=1580"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/tags?post=1580"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}