{"id":51,"date":"2020-02-10T13:55:57","date_gmt":"2020-02-10T12:55:57","guid":{"rendered":"http:\/\/blog.ara-test.pl\/?p=51"},"modified":"2021-10-21T10:34:50","modified_gmt":"2021-10-21T08:34:50","slug":"cqrs-spring-boot-example","status":"publish","type":"post","link":"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/","title":{"rendered":"CQRS - Spring Boot application example"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minut<\/span><\/span><p class=\"wp-block-paragraph\"><strong>CQRS &#8211; Command Query Responsibility Segregation<\/strong>. Sama nazwa ju&#380; mo&#380;e przysporzy&#263; zawrotu g&#322;owy. Gdy pierwszy raz us&#322;ysza&#322;em o tym wzorcu projektowym to od razu w mojej g&#322;owie pojawi&#322;y si&#281; my&#347;li typu &#8222;Wow! Du&#380;o literek w skr&oacute;cie wi&#281;c pewnie trudne&#8230;&#8221; Nic bardziej mylnego!<\/p>\n\n\n<h3 class=\"wp-block-heading wp-block-heading\" id=\"historia-cqrs\">Historia CQRS<\/h3>\n\n\n<p class=\"wp-block-paragraph\">Po raz pierwszy o CQRS &#347;wiat us&#322;ysza&#322; w 2010 roku dzi&#281;ki Gregowi Youngowi w &bdquo;CQRS Documents by Greg Young&rdquo; (<a href=\"https:\/\/cqrs.files.wordpress.com\/2010\/11\/cqrs_documents.pdf\">https:\/\/cqrs.files.wordpress.com\/2010\/11\/cqrs_documents.pdf<\/a>). Greg opisuje tam cz&#281;sto spotykane problemy jakie mog&#261; wyst&#281;powa&#263; podczas developmentu aplikacji o architekturze warstwowej. Architektura warstwowa MVC (Model View Controller) wydaje si&#281; by&#263; stosunkowo dobrym wyborem podczas implementacji aplikacji, ale sama w sobie niestety nie chroni nas przed implementowaniem klas typu &#8222;spagetti code&#8221;, czy te&#380; niedbalstwem o wydajno&#347;&#263; aplikacji. I tutaj z pomoc&#261; przychodzi nasz tajemniczy CQRS ze swoim logicznym podzia&#322;em kodu na dwie sekcje: &#8222;queries&#8221; i &#8222;commands&#8221;.<\/p>\n\n\n<h3 class=\"wp-block-heading wp-block-heading\" id=\"czym-jest-cqrs\">Czym jest CQRS?<\/h3>\n\n\n<p class=\"wp-block-paragraph\">W CQRS chodzi o to aby oddzieli&#263; od siebie te cz&#281;&#347;ci kodu, kt&oacute;re modyfikuj&#261; stan obiekt&oacute;w (czyli commands) od tych, kt&oacute;re tylko udost&#281;pniaj&#261; dane (czyli queries).  Taka organizacja kodu w projekcie znacznie upraszcza jego czytelno&#347;&#263;. Od razu widzimy za co odpowiadaj&#261; poszczeg&oacute;lne klasy. Stanowi tak&#380;e spore pole do optymalizacji zapyta&#324; do bazy danych. Ponad to, my, jako programi&#347;ci, jeste&#347;my zmuszeni do zachowania pojedynczej odpowiedzialno&#347;ci klasy. Co za tym idzie mo&#380;emy unika&#263; tak zwanych klas &#8222;tasiemc&oacute;w&#8221;, kt&oacute;rych d&#322;ugo&#347;&#263; cz&#281;sto przekracza nawet kilka tysi&#281;cy linii kodu! Poni&#380;ej znajduje si&#281; prosty przyk&#322;ad, przedstawiaj&#261;cy przep&#322;yw danych w aplikacji z wykorzystaniem CQRS.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"697\" height=\"304\" src=\"https:\/\/arasoftware.pl\/wp-content\/uploads\/2020\/02\/a6945122-1531-47bc-81d3-74d2952f1d88.png\" alt=\"\" class=\"wp-image-58\" srcset=\"https:\/\/arasoftware.pl\/wp-content\/uploads\/2020\/02\/a6945122-1531-47bc-81d3-74d2952f1d88.png 697w, https:\/\/arasoftware.pl\/wp-content\/uploads\/2020\/02\/a6945122-1531-47bc-81d3-74d2952f1d88-300x131.png 300w\" sizes=\"auto, (max-width: 697px) 100vw, 697px\" \/><\/figure>\n\n\n<h3 class=\"wp-block-heading wp-block-heading\" id=\"przykc582ad-aplikacji\">Przyk&#322;ad aplikacji<\/h3>\n\n\n<p class=\"wp-block-paragraph has-text-align-left\">Z racji, &#380;e <strong>Kotlin ma zast&#261;pi&#263; w najbli&#380;szej przysz&#322;o&#347;ci JAV&#280;<\/strong> na niekt&oacute;rych polach, to uzna&#322;em &#380;e b&#281;dzie dobrym j&#281;zykiem, aby u&#380;ywa&#263; go do przyk&#322;ad&oacute;w w moich postach na tym blogu. Wi&#281;cej o Kotlinie mo&#380;esz znale&#378;&#263; na <a rel=\"noreferrer noopener\" aria-label=\" (otwiera si&#281; na nowej zak&#322;adce)\" href=\"https:\/\/kotlinlang.org\" target=\"_blank\">https:\/\/kotlinlang.org<\/a>. Aby u&#322;atwi&#263; nie co development aplikacji, w przyk&#322;adzie zostanie u&#380;yty Spring Boot. Pozwoli nam to na uruchomienie aplikacji w relatywnie prosty spos&oacute;b i sprawdzenie czy nasz kod dzia&#322;a!<\/p>\n\n\n<p class=\"wp-block-paragraph\">Nasza przyk&#322;adowa aplikacja b&#281;dzie odpowiada&#322;a za zarz&#261;dzanie list&#261; os&oacute;b. Wiem, to nie jest nic odkrywczego ale, nie o to tutaj chodzi. Skupmy si&#281; raczej na samej implementacji CQRS i na tym, co mo&#380;emy dzi&#281;ki osi&#261;gn&#261;&#263;.<\/p>\n\n\n<p class=\"wp-block-paragraph\">Na stronie <a rel=\"noreferrer noopener\" aria-label=\" (otwiera si&#281; na nowej zak&#322;adce)\" href=\"https:\/\/start.spring.io\/\" target=\"_blank\">https:\/\/start.spring.io\/<\/a> mo&#380;na wygenerowa&#263; projekt, kt&oacute;ry b&#281;dzie zawiera&#322; wszystkie niezb&#281;dne dla naszej aplikacji zale&#380;no&#347;ci. Ja wybra&#322;em tak&#261; konfiguracj&#281;:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"904\" src=\"https:\/\/arasoftware.pl\/wp-content\/uploads\/2020\/03\/image-1024x904.png\" alt=\"\" class=\"wp-image-88\" srcset=\"https:\/\/arasoftware.pl\/wp-content\/uploads\/2020\/03\/image-1024x904.png 1024w, https:\/\/arasoftware.pl\/wp-content\/uploads\/2020\/03\/image-300x265.png 300w, https:\/\/arasoftware.pl\/wp-content\/uploads\/2020\/03\/image-768x678.png 768w, https:\/\/arasoftware.pl\/wp-content\/uploads\/2020\/03\/image.png 1276w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n<p class=\"wp-block-paragraph\">Doda&#322;em trzy zale&#380;no&#347;ci do projektu:<\/p>\n\n\n<ul class=\"wp-block-list wp-block-list\" style=\"\">\n<li>String Data JPA &#8211; dzi&#281;ki temu b&#281;dziemy w mogli w bardzo &#322;atwy spos&oacute;b wykonywa&#263; zapytania do bazy danych. Za pomoc&#261; jednego interfejsu Spring Data dostarczy nam szereg najcz&#281;&#347;ciej u&#380;ywanych metod takich jak save, delete, findOneById i wiele, wiele innych.<\/li>\n<li>Spring Web &#8211; podniesie kontekst webowy aplikacji, tak aby by&#322;a mo&#380;liwo&#347;&#263; wystawienia kilku endpoint&#8217;&oacute;w REST-owych.<\/li>\n<li>H2 Database &#8211; Relacyjna baza danych, kt&oacute;ra jest uruchamiana automatycznie w pami&#281;ci operacyjnej zaraz po starcie aplikacji. <\/li>\n<\/ul>\n\n\n<p class=\"wp-block-paragraph\">W pliku pom.xml powinno to wygl&#261;da&#263; mniej wi&#281;cej tak jak poni&#380;ej:<\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"pom.xml\"><code lang=\"xml\" class=\"language-xml line-numbers\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?>\n&lt;project xmlns=\"http:\/\/maven.apache.org\/POM\/4.0.0\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"\n         xsi:schemaLocation=\"http:\/\/maven.apache.org\/POM\/4.0.0 https:\/\/maven.apache.org\/xsd\/maven-4.0.0.xsd\">\n    &lt;modelVersion>4.0.0&lt;\/modelVersion>\n    &lt;parent>\n        &lt;groupId>org.springframework.boot&lt;\/groupId>\n        &lt;artifactId>spring-boot-starter-parent&lt;\/artifactId>\n        &lt;version>2.2.5.RELEASE&lt;\/version>\n        &lt;relativePath\/> &lt;!-- lookup parent from repository -->\n    &lt;\/parent>\n    &lt;groupId>com.ara&lt;\/groupId>\n    &lt;artifactId>cqrs&lt;\/artifactId>\n    &lt;version>0.0.1-SNAPSHOT&lt;\/version>\n    &lt;name>cqrs&lt;\/name>\n    &lt;description>CQRS Demo Application&lt;\/description>\n\n    &lt;properties>\n        &lt;java.version>1.8&lt;\/java.version>\n        &lt;kotlin.version>1.3.61&lt;\/kotlin.version>\n    &lt;\/properties>\n\n    &lt;dependencies>\n        &lt;dependency>\n            &lt;groupId>org.springframework.boot&lt;\/groupId>\n            &lt;artifactId>spring-boot-starter-data-jpa&lt;\/artifactId>\n        &lt;\/dependency>\n        &lt;dependency>\n            &lt;groupId>org.springframework.boot&lt;\/groupId>\n            &lt;artifactId>spring-boot-starter-web&lt;\/artifactId>\n        &lt;\/dependency>\n        &lt;dependency>\n            &lt;groupId>com.fasterxml.jackson.module&lt;\/groupId>\n            &lt;artifactId>jackson-module-kotlin&lt;\/artifactId>\n        &lt;\/dependency>\n        &lt;dependency>\n            &lt;groupId>org.jetbrains.kotlin&lt;\/groupId>\n            &lt;artifactId>kotlin-reflect&lt;\/artifactId>\n        &lt;\/dependency>\n        &lt;dependency>\n            &lt;groupId>org.jetbrains.kotlin&lt;\/groupId>\n            &lt;artifactId>kotlin-stdlib-jdk8&lt;\/artifactId>\n        &lt;\/dependency>\n\n        &lt;dependency>\n            &lt;groupId>com.h2database&lt;\/groupId>\n            &lt;artifactId>h2&lt;\/artifactId>\n            &lt;scope>runtime&lt;\/scope>\n        &lt;\/dependency>\n        &lt;dependency>\n            &lt;groupId>org.springframework.boot&lt;\/groupId>\n            &lt;artifactId>spring-boot-starter-test&lt;\/artifactId>\n            &lt;scope>test&lt;\/scope>\n            &lt;exclusions>\n                &lt;exclusion>\n                    &lt;groupId>org.junit.vintage&lt;\/groupId>\n                    &lt;artifactId>junit-vintage-engine&lt;\/artifactId>\n                &lt;\/exclusion>\n            &lt;\/exclusions>\n        &lt;\/dependency>\n    &lt;\/dependencies>\n\n    &lt;build>\n        &lt;sourceDirectory>${project.basedir}\/src\/main\/kotlin&lt;\/sourceDirectory>\n        &lt;testSourceDirectory>${project.basedir}\/src\/test\/kotlin&lt;\/testSourceDirectory>\n        &lt;plugins>\n            &lt;plugin>\n                &lt;groupId>org.springframework.boot&lt;\/groupId>\n                &lt;artifactId>spring-boot-maven-plugin&lt;\/artifactId>\n            &lt;\/plugin>\n            &lt;plugin>\n                &lt;groupId>org.jetbrains.kotlin&lt;\/groupId>\n                &lt;artifactId>kotlin-maven-plugin&lt;\/artifactId>\n                &lt;configuration>\n                    &lt;args>\n                        &lt;arg>-Xjsr305=strict&lt;\/arg>\n                    &lt;\/args>\n                    &lt;compilerPlugins>\n                        &lt;plugin>spring&lt;\/plugin>\n                        &lt;plugin>jpa&lt;\/plugin>\n                    &lt;\/compilerPlugins>\n                &lt;\/configuration>\n                &lt;dependencies>\n                    &lt;dependency>\n                        &lt;groupId>org.jetbrains.kotlin&lt;\/groupId>\n                        &lt;artifactId>kotlin-maven-allopen&lt;\/artifactId>\n                        &lt;version>${kotlin.version}&lt;\/version>\n                    &lt;\/dependency>\n                    &lt;dependency>\n                        &lt;groupId>org.jetbrains.kotlin&lt;\/groupId>\n                        &lt;artifactId>kotlin-maven-noarg&lt;\/artifactId>\n                        &lt;version>${kotlin.version}&lt;\/version>\n                    &lt;\/dependency>\n                &lt;\/dependencies>\n            &lt;\/plugin>\n        &lt;\/plugins>\n    &lt;\/build>\n\n&lt;\/project><\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading wp-block-heading\" id=\"implementacja\">Implementacja<\/h3>\n\n\n<p class=\"wp-block-paragraph\">W pakiecie &#8222;com.ara&#8221; znajduj&#281; si&#281; klasa CqrsApplication. Odpowiada ona za start ca&#322;ej aplikacji Spring Boot.<\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"CqrsApplication.kt\"><code lang=\"kotlin\" class=\"language-kotlin line-numbers\">package com.ara\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication\nimport org.springframework.boot.runApplication\n\n@SpringBootApplication\nclass CqrsApplication\n\nfun main(args: Array&lt;String>) {\n    runApplication&lt;CqrsApplication>(*args)\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Skoro nasza aplikacja ma za zadanie zarz&#261;dzanie list&#261; os&oacute;b, to zaimplementujmy obiekt, kt&oacute;ry b&#281;dzie przechowywa&#322; dane pojedynczej osoby. Poni&#380;sza klasa Person.kt to nic innego jak zwyk&#322;e POJO\/Encja z trzeba polami: <\/p>\n\n\n<ul class=\"wp-block-list wp-block-list\" style=\"\">\n<li>name<\/li>\n<li>age<\/li>\n<li>id <\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\" title=\"Person.kt\"><code lang=\"kotlin\" class=\"language-kotlin line-numbers\">package com.ara.mvc.model\n\nimport javax.persistence.Entity\nimport javax.persistence.GeneratedValue\nimport javax.persistence.Id\n\n@Entity\ndata class Person(var name: String,\n                  val age: Integer,\n                  @Id @GeneratedValue var id: Long? = null)<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">No i tutaj zaczyna si&#281; ca&#322;e mi&#281;so naszej aplikacji \ud83d\ude00 Jak wspomnia&#322;em wcze&#347;niej CQRS dzieli funkcjonalno&#347;ci aplikacji na dwie sekcje. Pierwsza z nich to Commands. Sekcja ta powinna zawiera&#263; tylko takie funkcjonalno&#347;ci, kt&oacute;re faktycznie zmieniaj&#261; stan obiekt&oacute;w i nie s&#322;u&#380;&#261; do pobierania danych. Utw&oacute;rzmy zatem serwis w formie interfejsu, kt&oacute;ry b&#281;dzie posiada&#322; deklaracje dw&oacute;ch metod: save() oraz delete(). <\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"PersonCommandService.kt\"><code lang=\"kotlin\" class=\"language-kotlin line-numbers\">package com.ara.cqrs.service\n\nimport com.ara.model.Person\nimport org.springframework.stereotype.Service\n\n@Service\ninterface PersonCommandService {\n\n    fun save(person: Person)\n    fun delete(name: String)\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Implementacja powy&#380;szego interfejsu PersonCommandService.kt b&#281;dzie dostarcza&#263; te dwie metody. Jak wida&#263; &#380;adna z nich nie zwraca warto&#347;ci i nie jeste&#347;my w stanie u&#380;y&#263; ich do pobierania informacji o osobach z bazy danych. Bardzo dobrze wida&#263; tutaj zachowan&#261; tak zwan&#261; &#8222;pojedyncz&#261; odpowiedzialno&#347;&#263;&#8221;.<\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"PersonCommandServiceImpl.kt\"><code lang=\"kotlin\" class=\"language-kotlin line-numbers\">package com.ara.cqrs.service.impl\n\nimport com.ara.cqrs.repository.PersonCommandRepository\nimport com.ara.cqrs.service.PersonCommandService\nimport com.ara.cqrs.service.PersonQueryService\nimport com.ara.model.Person\nimport org.springframework.stereotype.Service\n\n@Service\nclass PersonCommandServiceImpl(private val personCommandRepository: PersonCommandRepository,\n                               private val personQueryService: PersonQueryService) : PersonCommandService {\n\n    override fun save(person: Person) {\n        personCommandRepository.save(person)\n    }\n\n    override fun delete(name: String) {\n        val person : Person = personQueryService.findByName(name)\n        personCommandRepository.delete(person)\n    }\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Stw&oacute;rzmy jeszcze repozytorium z wykorzystaniem Spring Data na potrzeby naszego serwisu, obs&#322;uguj&#261;cego komendy.<\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"PersonCommandRepository.kt\"><code lang=\"kotlin\" class=\"language-kotlin line-numbers\">package com.ara.cqrs.repository\n\nimport com.ara.model.Person\nimport org.springframework.data.repository.CrudRepository\nimport org.springframework.stereotype.Repository\n\n@Repository\ninterface PersonCommandRepository : CrudRepository&lt;Person, Long> {\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Analogicznie sprawa wygl&#261;da w przypadku kolejnej sekcji, kt&oacute;r&#261; jest &#8222;Queries&#8221;. Tutaj sprawa wygl&#261;da odwrotnie. W sekcji &#8222;Queries&#8221; mamy do czynienia tylko z funkcjonalno&#347;ciami, kt&oacute;re nie zmieniaj&#261; stan&oacute;w obiekt&oacute;w, a jedynie serwuj&#261; dane o nich. Czyli poni&#380;sza klasa PersonQueryService.kt tak&#380;e udost&#281;pnia dwie definicje metod, ale ka&#380;da z nich s&#322;u&#380;y do pobierania danych, a nie ich edycji. <\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"PersonQueryService.kt\"><code lang=\"kotlin\" class=\"language-kotlin line-numbers\">package com.ara.cqrs.service\n\nimport com.ara.model.Person\nimport org.springframework.stereotype.Service\n\n@Service\ninterface PersonQueryService {\n\n    fun findAll(): MutableIterable&lt;Person>\n    fun findByName(name: String) : Person\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Implementacja powy&#380;szego interfejsu mo&#380;e wygl&#261;da&#263; nast&#281;puj&#261;co:<\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"PersonQueryServiceImpl.kt\"><code lang=\"kotlin\" class=\"language-kotlin line-numbers\">package com.ara.cqrs.service.impl\n\nimport com.ara.cqrs.repository.PersonQueryRepository\nimport com.ara.cqrs.service.PersonQueryService\nimport com.ara.model.Person\nimport org.springframework.stereotype.Service\nimport java.util.*\n\n@Service\nclass PersonQueryServiceImpl(private val personQueryRepository: PersonQueryRepository) : PersonQueryService {\n\n    override fun findAll(): MutableIterable&lt;Person> {\n        return personQueryRepository.findAll()\n    }\n\n    override fun findByName(name: String): Person {\n        var result : Optional&lt;Person> = personQueryRepository.findByName(name)\n        if (result.isPresent)\n            return result.get()\n        else\n            throw RuntimeException(\"Can not find person with name ${name}\")\n    }\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">No i tworzymy analogicznie repozytorium do obs&#322;ugi komunikacji z baz&#261; danych dla serwisu, zajmuj&#261;cego si&#281; operacjami typu &#8222;Query&#8221;.<\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"PersonQueryRepository.kt\"><code lang=\"kotlin\" class=\"language-kotlin line-numbers\">package com.ara.cqrs.repository\n\nimport com.ara.model.Person\nimport org.springframework.data.repository.CrudRepository\nimport org.springframework.stereotype.Repository\nimport java.util.*\n\n@Repository\ninterface PersonQueryRepository: CrudRepository&lt;Person, Long> {\n\n    fun findByName(name : String) : Optional&lt;Person>\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">W klasie PersonQueryServiceImpl.kt nie ma mo&#380;liwo&#347;ci zmiany stanu obiektu Person. I o to chodzi! Programista, korzystaj&#261;cy z takiego kodu, nie musi si&#281; zastanawia&#263;, czy je&#347;li wywo&#322;a jak&#261;&#347; metod&#281;, kt&oacute;ra teoretycznie powinna mu tylko zwr&oacute;ci&#263; dane, to nie zmieni stanu jakiego&#347; obiektu. Poza tym mo&#380;na p&oacute;j&#347;&#263; krok dalej i napisa&#263; specjalnie zoptymalizowane zapytania SQL do bazy danych, aby przyspieszy&#263; prac&#281; aplikacji Ale to ju&#380; temat na kolejny post \ud83d\ude42<\/p>\n\n\n<p class=\"wp-block-paragraph\">Ok, skoro mamy ju&#380; zaimplementowan&#261; warstw&#281; serwisu i logiki biznesowej, to czas na stworzenie kontroler&oacute;w, kt&oacute;re udost&#281;pni&#261; nam dost&#281;p do metod serwisowych. Jako pierwszy stw&oacute;rzmy kontroler obs&#322;uguj&#261;cy zdarzenia typu &#8222;Command&#8221;<\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"PersonCommandController.kt\"><code lang=\"kotlin\" class=\"language-kotlin line-numbers\">package com.ara.cqrs.controller\n\nimport com.ara.cqrs.service.PersonCommandService\nimport com.ara.model.Person\nimport org.springframework.web.bind.annotation.*\n\n@RestController\n@RequestMapping(\"\/cqrs\/command\")\nclass PersonCommandController (private val personCommandService: PersonCommandService) {\n\n    @PostMapping(\"\/person\")\n    fun savePerson(@RequestBody person: Person) = personCommandService.save(person)\n\n    @DeleteMapping(\"\/person\")\n    fun deletePerson(@RequestParam(value = \"name\") name: String) = personCommandService.delete(name)\n}<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">I analogicznie tworzymy kolejny kontroler z obs&#322;ug&#261; zdarze&#324; typu &#8222;Query&#8221;<\/p>\n\n\n\n<pre class=\"wp-block-code\" title=\"PersonQueryController.kt\"><code lang=\"kotlin\" class=\"language-kotlin line-numbers\">package com.ara.cqrs.controller\n\nimport com.ara.cqrs.service.PersonQueryService\nimport com.ara.model.Person\nimport org.springframework.web.bind.annotation.*\n\n@RestController\n@RequestMapping(\"\/cqrs\/query\")\nclass PersonQueryController(private val personQueryService: PersonQueryService) {\n\n    @GetMapping(\"\/person\/all\")\n    fun findAll() : MutableIterable&lt;Person> = personQueryService.findAll()\n\n    @GetMapping(\"\/person\")\n    fun getPersonById(@RequestParam(value = \"name\") name: String) : Person = personQueryService.findByName(name)\n\n}<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading wp-block-heading\" id=\"podsumowanie\">Podsumowanie<\/h3>\n\n\n<p class=\"wp-block-paragraph\">Jak wida&#263; na przedstawionym przyk&#322;adzie, CQRS wyra&#378;nie rozdziela aplikacj&#281; na dwie odr&#281;bne sekcje. Dzi&#281;ki temu znacznie &#322;atwiej utrzyma&#263; &#8222;czysto&#347;&#263;&#8221; kodu. Zmniejsza si&#281; tak&#380;e pr&oacute;g wej&#347;cia do pracy nad projektem z wykorzystaniem CQRS. Nowi programi&#347;ci maj&#261; znaczniej mniejsze szanse na przypadkowe (wynikaj&#261;ce z braku wiedzy biznesowej) wywo&#322;anie kodu, kt&oacute;ry tyko z pozoru wykonuje jedn&#261; rzecz. Kolejnym plusem takiego podzia&#322;u jest mo&#380;liwo&#347;&#263; szubkiego skalowania aplikacji i jej modularyzacji. Bez wi&#281;kszych problem&oacute;w jeste&#347;my w stanie wydzieli&#263; cz&#281;&#347;&#263; logiki biznesowej do nowego mikroserwisu lub modu&#322;u.<\/p>\n\n\n\n\n\n<p class=\"wp-block-paragraph\">Ca&#322;y kod aplikacji jest do pobrania z mojego Githuba (<a aria-label=\" (otwiera si&#281; na nowej zak&#322;adce)\" rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/radek-osak\/kotlin-cqrs-vs-mvc\" target=\"_blank\">https:\/\/github.com\/radek-osak\/kotlin-cqrs-vs-mvc<\/a>)<\/p>\n\n\n<p class=\"wp-block-paragraph\">Inne przydatne materia&#322;y:<\/p>\n\n\n<ul class=\"wp-block-list wp-block-list\" style=\"\"><li><a href=\"https:\/\/arasoftware.pl\/2020\/07\/01\/programowanie-reaktywne\/\">https:\/\/arasoftware.pl\/2020\/07\/01\/programowanie-reaktywne\/<\/a><\/li><\/ul>\n\n\n<p class=\"wp-block-paragraph\">Mam nadziej&#281;, &#380;e podoba&#322; Ci si&#281; ten post i jeszcze nie raz zawitasz na mojego bloga w poszukiwaniu wiedzy na temat programowania i architektury aplikacji. <\/p>\n\n\n<p class=\"wp-block-paragraph\">B&#281;d&#281; bardzo wdzi&#281;czny za pozostawienie komentarza oraz udost&#281;pnienie tego posta w social mediach \ud83d\ude42<\/p>","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minut<\/span><\/span> CQRS &#8211; Command Query Responsibility Segregation. Sama nazwa ju&#380; mo&#380;e przysporzy&#263; zawrotu g&#322;owy. Gdy pierwszy raz us&#322;ysza&#322;em o tym wzorcu projektowym to od razu w mojej g&#322;owie pojawi&#322;y si&#281; my&#347;li typu &#8222;Wow! Du&#380;o literek w skr&oacute;cie wi&#281;c pewnie trudne&#8230;&#8221; Nic bardziej mylnego! Historia CQRS Po raz pierwszy o CQRS &#347;wiat us&#322;ysza&#322; w 2010 roku dzi&#281;ki &hellip;<\/p>","protected":false},"author":1,"featured_media":951,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,13,19],"tags":[8,2,4,6,5],"class_list":["post-51","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-architektura-oprogramowania","category-programowanie","category-spring-boot","tag-architektura-oprogramowania","tag-cqrs","tag-kotlin","tag-mvc","tag-wzorce-projektowe"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.11 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>CQRS - przyk\u0142ad aplikacji w Spring Boot - Ara Software<\/title>\n<meta name=\"description\" content=\"W CQRS chodzi o to aby oddzieli\u0107 od siebie te cz\u0119\u015bci kodu, kt\u00f3re modyfikuj\u0105 stan obiekt\u00f3w (czyli commands) od tych, kt\u00f3re tylko udost\u0119pniaj\u0105 dane (czyli queries).\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"CQRS - przyk\u0142ad aplikacji w Spring Boot - Ara Software\" \/>\n<meta property=\"og:description\" content=\"W CQRS chodzi o to aby oddzieli\u0107 od siebie te cz\u0119\u015bci kodu, kt\u00f3re modyfikuj\u0105 stan obiekt\u00f3w (czyli commands) od tych, kt\u00f3re tylko udost\u0119pniaj\u0105 dane (czyli queries).\" \/>\n<meta property=\"og:url\" content=\"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/\" \/>\n<meta property=\"og:site_name\" content=\"Ara Software\" \/>\n<meta property=\"article:published_time\" content=\"2020-02-10T12:55:57+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-10-21T08:34:50+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/arasoftware.pl\/wp-content\/uploads\/2020\/07\/CQRS.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1080\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Radek Osak\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Radek Osak\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/\"},\"author\":{\"name\":\"Radek Osak\",\"@id\":\"https:\/\/arasoftware.pl\/#\/schema\/person\/a13fe68bd27e762ef255547a88956902\"},\"headline\":\"CQRS &#8211; przyk\u0142ad aplikacji w Spring Boot\",\"datePublished\":\"2020-02-10T12:55:57+00:00\",\"dateModified\":\"2021-10-21T08:34:50+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/\"},\"wordCount\":1129,\"publisher\":{\"@id\":\"https:\/\/arasoftware.pl\/#organization\"},\"keywords\":[\"Architektura oprogramowania\",\"CQRS\",\"Kotlin\",\"MVC\",\"Wzorce projektowe\"],\"articleSection\":[\"Architektura oprogramowania\",\"Programowanie\",\"Spring Boot\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/\",\"url\":\"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/\",\"name\":\"CQRS - przyk\u0142ad aplikacji w Spring Boot - Ara Software\",\"isPartOf\":{\"@id\":\"https:\/\/arasoftware.pl\/#website\"},\"datePublished\":\"2020-02-10T12:55:57+00:00\",\"dateModified\":\"2021-10-21T08:34:50+00:00\",\"description\":\"W CQRS chodzi o to aby oddzieli\u0107 od siebie te cz\u0119\u015bci kodu, kt\u00f3re modyfikuj\u0105 stan obiekt\u00f3w (czyli commands) od tych, kt\u00f3re tylko udost\u0119pniaj\u0105 dane (czyli queries).\",\"breadcrumb\":{\"@id\":\"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Strona g\u0142\u00f3wna\",\"item\":\"https:\/\/arasoftware.pl\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"CQRS &#8211; przyk\u0142ad aplikacji w Spring Boot\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/arasoftware.pl\/#website\",\"url\":\"https:\/\/arasoftware.pl\/\",\"name\":\"Ara Software\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/arasoftware.pl\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/arasoftware.pl\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/arasoftware.pl\/#organization\",\"name\":\"Ara Software\",\"url\":\"https:\/\/arasoftware.pl\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/arasoftware.pl\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/arasoftware.pl\/wp-content\/uploads\/2023\/06\/ara-logo-light-1.svg\",\"contentUrl\":\"https:\/\/arasoftware.pl\/wp-content\/uploads\/2023\/06\/ara-logo-light-1.svg\",\"width\":317,\"height\":55,\"caption\":\"Ara Software\"},\"image\":{\"@id\":\"https:\/\/arasoftware.pl\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/arasoftware.pl\/#\/schema\/person\/a13fe68bd27e762ef255547a88956902\",\"name\":\"Radek Osak\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/arasoftware.pl\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/2a5a97fd24033c3df7dff799dc95774b?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/2a5a97fd24033c3df7dff799dc95774b?s=96&d=mm&r=g\",\"caption\":\"Radek Osak\"},\"url\":\"https:\/\/arasoftware.pl\/en\/author\/radek030790gmail-com\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"CQRS - przyk\u0142ad aplikacji w Spring Boot - Ara Software","description":"W CQRS chodzi o to aby oddzieli\u0107 od siebie te cz\u0119\u015bci kodu, kt\u00f3re modyfikuj\u0105 stan obiekt\u00f3w (czyli commands) od tych, kt\u00f3re tylko udost\u0119pniaj\u0105 dane (czyli queries).","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:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/","og_locale":"en_US","og_type":"article","og_title":"CQRS - przyk\u0142ad aplikacji w Spring Boot - Ara Software","og_description":"W CQRS chodzi o to aby oddzieli\u0107 od siebie te cz\u0119\u015bci kodu, kt\u00f3re modyfikuj\u0105 stan obiekt\u00f3w (czyli commands) od tych, kt\u00f3re tylko udost\u0119pniaj\u0105 dane (czyli queries).","og_url":"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/","og_site_name":"Ara Software","article_published_time":"2020-02-10T12:55:57+00:00","article_modified_time":"2021-10-21T08:34:50+00:00","og_image":[{"width":1920,"height":1080,"url":"https:\/\/arasoftware.pl\/wp-content\/uploads\/2020\/07\/CQRS.png","type":"image\/png"}],"author":"Radek Osak","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Radek Osak","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/#article","isPartOf":{"@id":"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/"},"author":{"name":"Radek Osak","@id":"https:\/\/arasoftware.pl\/#\/schema\/person\/a13fe68bd27e762ef255547a88956902"},"headline":"CQRS &#8211; przyk\u0142ad aplikacji w Spring Boot","datePublished":"2020-02-10T12:55:57+00:00","dateModified":"2021-10-21T08:34:50+00:00","mainEntityOfPage":{"@id":"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/"},"wordCount":1129,"publisher":{"@id":"https:\/\/arasoftware.pl\/#organization"},"keywords":["Architektura oprogramowania","CQRS","Kotlin","MVC","Wzorce projektowe"],"articleSection":["Architektura oprogramowania","Programowanie","Spring Boot"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/","url":"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/","name":"CQRS - przyk\u0142ad aplikacji w Spring Boot - Ara Software","isPartOf":{"@id":"https:\/\/arasoftware.pl\/#website"},"datePublished":"2020-02-10T12:55:57+00:00","dateModified":"2021-10-21T08:34:50+00:00","description":"W CQRS chodzi o to aby oddzieli\u0107 od siebie te cz\u0119\u015bci kodu, kt\u00f3re modyfikuj\u0105 stan obiekt\u00f3w (czyli commands) od tych, kt\u00f3re tylko udost\u0119pniaj\u0105 dane (czyli queries).","breadcrumb":{"@id":"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/arasoftware.pl\/en\/cqrs-spring-boot-example\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Strona g\u0142\u00f3wna","item":"https:\/\/arasoftware.pl\/"},{"@type":"ListItem","position":2,"name":"CQRS &#8211; przyk\u0142ad aplikacji w Spring Boot"}]},{"@type":"WebSite","@id":"https:\/\/arasoftware.pl\/#website","url":"https:\/\/arasoftware.pl\/","name":"Ara Software","description":"","publisher":{"@id":"https:\/\/arasoftware.pl\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/arasoftware.pl\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/arasoftware.pl\/#organization","name":"Ara Software","url":"https:\/\/arasoftware.pl\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/arasoftware.pl\/#\/schema\/logo\/image\/","url":"https:\/\/arasoftware.pl\/wp-content\/uploads\/2023\/06\/ara-logo-light-1.svg","contentUrl":"https:\/\/arasoftware.pl\/wp-content\/uploads\/2023\/06\/ara-logo-light-1.svg","width":317,"height":55,"caption":"Ara Software"},"image":{"@id":"https:\/\/arasoftware.pl\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/arasoftware.pl\/#\/schema\/person\/a13fe68bd27e762ef255547a88956902","name":"Radek Osak","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/arasoftware.pl\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/2a5a97fd24033c3df7dff799dc95774b?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/2a5a97fd24033c3df7dff799dc95774b?s=96&d=mm&r=g","caption":"Radek Osak"},"url":"https:\/\/arasoftware.pl\/en\/author\/radek030790gmail-com\/"}]}},"_links":{"self":[{"href":"https:\/\/arasoftware.pl\/en\/wp-json\/wp\/v2\/posts\/51","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/arasoftware.pl\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/arasoftware.pl\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/arasoftware.pl\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/arasoftware.pl\/en\/wp-json\/wp\/v2\/comments?post=51"}],"version-history":[{"count":0,"href":"https:\/\/arasoftware.pl\/en\/wp-json\/wp\/v2\/posts\/51\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/arasoftware.pl\/en\/wp-json\/wp\/v2\/media\/951"}],"wp:attachment":[{"href":"https:\/\/arasoftware.pl\/en\/wp-json\/wp\/v2\/media?parent=51"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/arasoftware.pl\/en\/wp-json\/wp\/v2\/categories?post=51"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/arasoftware.pl\/en\/wp-json\/wp\/v2\/tags?post=51"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}