29
									
								
								cqrs/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								cqrs/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | --- | ||||||
|  | layout: pattern | ||||||
|  | title: CQRS | ||||||
|  | folder: cqrs | ||||||
|  | permalink: /patterns/cqrs/ | ||||||
|  | pumlid: 7SPR4a0m3030gt00pR_RH6I8QQFouFgC_TfHb6gkd5Q7FQBx363ub4rYpoMTZKuDrYXqDX37HIuuyCPfPPTDfuuHREhGqBy0NUR0GNzAMYizMtq1 | ||||||
|  | categories: Architectural | ||||||
|  | tags: | ||||||
|  |  - Java | ||||||
|  |  - Difficulty-Intermediate | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Intent | ||||||
|  | CQRS Command Query Responsibility Segregation - Separate the query side from the command side. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Applicability | ||||||
|  | Use the CQRS pattern when | ||||||
|  |  | ||||||
|  | * you want to scale the queries and commands independently. | ||||||
|  | * you want to use different data models for queries and commands. Useful when dealing with complex domains. | ||||||
|  | * you want to use architectures like event sourcing or task based UI. | ||||||
|  |  | ||||||
|  | ## Credits | ||||||
|  |  | ||||||
|  | * [Greg Young - CQRS, Task Based UIs, Event Sourcing agh!](http://codebetter.com/gregyoung/2010/02/16/cqrs-task-based-uis-event-sourcing-agh/) | ||||||
|  | * [Martin Fowler - CQRS](https://martinfowler.com/bliki/CQRS.html) | ||||||
|  | * [Oliver Wolf - CQRS for Great Good](https://www.youtube.com/watch?v=Ge53swja9Dw) | ||||||
							
								
								
									
										
											BIN
										
									
								
								cqrs/etc/cqrs.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								cqrs/etc/cqrs.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 103 KiB | 
							
								
								
									
										115
									
								
								cqrs/etc/cqrs.ucls
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								cqrs/etc/cqrs.ucls
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <class-diagram version="1.2.0" icons="true" always-add-relationships="false" generalizations="true" realizations="true"  | ||||||
|  |   associations="true" dependencies="false" nesting-relationships="true" router="FAN">   | ||||||
|  |   <interface id="1" language="java" name="com.iluwatar.cqrs.commandes.ICommandService" project="cqrs"  | ||||||
|  |     file="/cqrs/src/main/java/com/iluwatar/cqrs/commandes/ICommandService.java" binary="false" corner="BOTTOM_RIGHT">     | ||||||
|  |     <position height="-1" width="-1" x="291" y="-49"/>     | ||||||
|  |     <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"  | ||||||
|  |       sort-features="false" accessors="true" visibility="true">       | ||||||
|  |       <attributes public="true" package="true" protected="true" private="true" static="true"/>       | ||||||
|  |       <operations public="true" package="true" protected="true" private="true" static="true"/>     | ||||||
|  |     </display>   | ||||||
|  |   </interface>   | ||||||
|  |   <class id="2" language="java" name="com.iluwatar.cqrs.commandes.CommandServiceImpl" project="cqrs"  | ||||||
|  |     file="/cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java" binary="false" corner="BOTTOM_RIGHT">     | ||||||
|  |     <position height="263" width="256" x="170" y="87"/>     | ||||||
|  |     <display autosize="false" stereotype="true" package="true" initial-value="false" signature="true"  | ||||||
|  |       sort-features="false" accessors="true" visibility="true">       | ||||||
|  |       <attributes public="true" package="true" protected="true" private="true" static="true"/>       | ||||||
|  |       <operations public="true" package="true" protected="true" private="true" static="true"/>     | ||||||
|  |     </display>   | ||||||
|  |   </class>   | ||||||
|  |   <interface id="3" language="java" name="com.iluwatar.cqrs.queries.IQueryService" project="cqrs"  | ||||||
|  |     file="/cqrs/src/main/java/com/iluwatar/cqrs/queries/IQueryService.java" binary="false" corner="BOTTOM_RIGHT">     | ||||||
|  |     <position height="182" width="248" x="176" y="428"/>     | ||||||
|  |     <display autosize="false" stereotype="true" package="true" initial-value="false" signature="true"  | ||||||
|  |       sort-features="false" accessors="true" visibility="true">       | ||||||
|  |       <attributes public="true" package="true" protected="true" private="true" static="true"/>       | ||||||
|  |       <operations public="true" package="true" protected="true" private="true" static="true"/>     | ||||||
|  |     </display>   | ||||||
|  |   </interface>   | ||||||
|  |   <class id="4" language="java" name="com.iluwatar.cqrs.queries.QueryServiceImpl" project="cqrs"  | ||||||
|  |     file="/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java" binary="false" corner="BOTTOM_RIGHT">     | ||||||
|  |     <position height="258" width="253" x="169" y="665"/>     | ||||||
|  |     <display autosize="false" stereotype="true" package="true" initial-value="false" signature="true"  | ||||||
|  |       sort-features="false" accessors="true" visibility="true">       | ||||||
|  |       <attributes public="true" package="true" protected="true" private="true" static="true"/>       | ||||||
|  |       <operations public="true" package="true" protected="true" private="true" static="true"/>     | ||||||
|  |     </display>   | ||||||
|  |   </class>   | ||||||
|  |   <class id="5" language="java" name="com.iluwatar.cqrs.domain.model.Book" project="cqrs"  | ||||||
|  |     file="/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Book.java" binary="false" corner="BOTTOM_RIGHT">     | ||||||
|  |     <position height="326" width="158" x="778" y="-93"/>     | ||||||
|  |     <display autosize="false" stereotype="true" package="true" initial-value="false" signature="true"  | ||||||
|  |       sort-features="false" accessors="true" visibility="true">       | ||||||
|  |       <attributes public="true" package="true" protected="true" private="true" static="true"/>       | ||||||
|  |       <operations public="true" package="true" protected="true" private="true" static="true"/>     | ||||||
|  |     </display>   | ||||||
|  |   </class>   | ||||||
|  |   <class id="6" language="java" name="com.iluwatar.cqrs.dto.Book" project="cqrs"  | ||||||
|  |     file="/cqrs/src/main/java/com/iluwatar/cqrs/dto/Book.java" binary="false" corner="BOTTOM_RIGHT">     | ||||||
|  |     <position height="219" width="150" x="541" y="607"/>     | ||||||
|  |     <display autosize="false" stereotype="true" package="true" initial-value="false" signature="true"  | ||||||
|  |       sort-features="false" accessors="true" visibility="true">       | ||||||
|  |       <attributes public="true" package="true" protected="true" private="true" static="true"/>       | ||||||
|  |       <operations public="true" package="true" protected="true" private="true" static="true"/>     | ||||||
|  |     </display>   | ||||||
|  |   </class>   | ||||||
|  |   <class id="7" language="java" name="com.iluwatar.cqrs.domain.model.Author" project="cqrs"  | ||||||
|  |     file="/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Author.java" binary="false" corner="BOTTOM_RIGHT">     | ||||||
|  |     <position height="-1" width="-1" x="608" y="70"/>     | ||||||
|  |     <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"  | ||||||
|  |       sort-features="false" accessors="true" visibility="true">       | ||||||
|  |       <attributes public="true" package="true" protected="true" private="true" static="true"/>       | ||||||
|  |       <operations public="true" package="true" protected="true" private="true" static="true"/>     | ||||||
|  |     </display>   | ||||||
|  |   </class>   | ||||||
|  |   <class id="8" language="java" name="com.iluwatar.cqrs.dto.Author" project="cqrs"  | ||||||
|  |     file="/cqrs/src/main/java/com/iluwatar/cqrs/dto/Author.java" binary="false" corner="BOTTOM_RIGHT">     | ||||||
|  |     <position height="-1" width="-1" x="834" y="719"/>     | ||||||
|  |     <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"  | ||||||
|  |       sort-features="false" accessors="true" visibility="true">       | ||||||
|  |       <attributes public="true" package="true" protected="true" private="true" static="true"/>       | ||||||
|  |       <operations public="true" package="true" protected="true" private="true" static="true"/>     | ||||||
|  |     </display>   | ||||||
|  |   </class>   | ||||||
|  |   <class id="9" language="java" name="com.iluwatar.cqrs.util.HibernateUtil" project="cqrs"  | ||||||
|  |     file="/cqrs/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java" binary="false" corner="BOTTOM_RIGHT">     | ||||||
|  |     <position height="-1" width="-1" x="662" y="412"/>     | ||||||
|  |     <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"  | ||||||
|  |       sort-features="false" accessors="true" visibility="true">       | ||||||
|  |       <attributes public="true" package="true" protected="true" private="true" static="true"/>       | ||||||
|  |       <operations public="true" package="true" protected="true" private="true" static="true"/>     | ||||||
|  |     </display>   | ||||||
|  |   </class>   | ||||||
|  |   <dependency id="10">     | ||||||
|  |     <end type="SOURCE" refId="4"/>     | ||||||
|  |     <end type="TARGET" refId="9"/>   | ||||||
|  |   </dependency>   | ||||||
|  |   <dependency id="11">     | ||||||
|  |     <end type="SOURCE" refId="2"/>     | ||||||
|  |     <end type="TARGET" refId="9"/>   | ||||||
|  |   </dependency>   | ||||||
|  |   <realization id="12">     | ||||||
|  |     <end type="SOURCE" refId="4"/>     | ||||||
|  |     <end type="TARGET" refId="3"/>   | ||||||
|  |   </realization>   | ||||||
|  |   <association id="13">     | ||||||
|  |     <end type="SOURCE" refId="5" navigable="false">       | ||||||
|  |       <attribute id="14" name="author"/>       | ||||||
|  |       <multiplicity id="15" minimum="0" maximum="1"/>     | ||||||
|  |     </end>     | ||||||
|  |     <end type="TARGET" refId="7" navigable="true"/>     | ||||||
|  |     <display labels="true" multiplicity="true"/>   | ||||||
|  |   </association>   | ||||||
|  |   <realization id="16">     | ||||||
|  |     <end type="SOURCE" refId="2"/>     | ||||||
|  |     <end type="TARGET" refId="1"/>   | ||||||
|  |   </realization>   | ||||||
|  |   <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"  | ||||||
|  |     sort-features="false" accessors="true" visibility="true">     | ||||||
|  |     <attributes public="true" package="true" protected="true" private="true" static="true"/>     | ||||||
|  |     <operations public="true" package="true" protected="true" private="true" static="true"/>   | ||||||
|  |   </classifier-display>   | ||||||
|  |   <association-display labels="true" multiplicity="true"/> | ||||||
|  | </class-diagram> | ||||||
							
								
								
									
										124
									
								
								cqrs/etc/cqrs.urm.puml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								cqrs/etc/cqrs.urm.puml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | @startuml | ||||||
|  | package com.iluwatar.cqrs.util { | ||||||
|  |   class HibernateUtil { | ||||||
|  |     - LOGGER : Logger {static} | ||||||
|  |     - SESSIONFACTORY : SessionFactory {static} | ||||||
|  |     + HibernateUtil() | ||||||
|  |     - buildSessionFactory() : SessionFactory {static} | ||||||
|  |     + getSessionFactory() : SessionFactory {static} | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | package com.iluwatar.cqrs.app { | ||||||
|  |   class App { | ||||||
|  |     + App() | ||||||
|  |     + main(args : String[]) {static} | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | package com.iluwatar.cqrs.dto { | ||||||
|  |   class Author { | ||||||
|  |     - email : String | ||||||
|  |     - name : String | ||||||
|  |     - username : String | ||||||
|  |     + Author() | ||||||
|  |     + Author(name : String, email : String, username : String) | ||||||
|  |     + equals(obj : Object) : boolean | ||||||
|  |     + getEmail() : String | ||||||
|  |     + getName() : String | ||||||
|  |     + getUsername() : String | ||||||
|  |     + hashCode() : int | ||||||
|  |     + toString() : String | ||||||
|  |   } | ||||||
|  |   class Book { | ||||||
|  |     - price : double | ||||||
|  |     - title : String | ||||||
|  |     + Book() | ||||||
|  |     + Book(title : String, price : double) | ||||||
|  |     + equals(obj : Object) : boolean | ||||||
|  |     + getPrice() : double | ||||||
|  |     + getTitle() : String | ||||||
|  |     + hashCode() : int | ||||||
|  |     + toString() : String | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | package com.iluwatar.cqrs.commandes { | ||||||
|  |   class CommandServiceImpl { | ||||||
|  |     - sessionFactory : SessionFactory | ||||||
|  |     + CommandServiceImpl() | ||||||
|  |     + authorCreated(username : String, name : String, email : String) | ||||||
|  |     + authorEmailUpdated(username : String, email : String) | ||||||
|  |     + authorNameUpdated(username : String, name : String) | ||||||
|  |     + authorUsernameUpdated(oldUsername : String, newUsername : String) | ||||||
|  |     + bookAddedToAuthor(title : String, price : double, username : String) | ||||||
|  |     + bookPriceUpdated(title : String, price : double) | ||||||
|  |     + bookTitleUpdated(oldTitle : String, newTitle : String) | ||||||
|  |     - getAuthorByUsername(username : String) : Author | ||||||
|  |     - getBookByTitle(title : String) : Book | ||||||
|  |   } | ||||||
|  |   interface ICommandService { | ||||||
|  |     + authorCreated(String, String, String) {abstract} | ||||||
|  |     + authorEmailUpdated(String, String) {abstract} | ||||||
|  |     + authorNameUpdated(String, String) {abstract} | ||||||
|  |     + authorUsernameUpdated(String, String) {abstract} | ||||||
|  |     + bookAddedToAuthor(String, double, String) {abstract} | ||||||
|  |     + bookPriceUpdated(String, double) {abstract} | ||||||
|  |     + bookTitleUpdated(String, String) {abstract} | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | package com.iluwatar.cqrs.queries { | ||||||
|  |   interface IQueryService { | ||||||
|  |     + getAuthorBooks(String) : List<Book> {abstract} | ||||||
|  |     + getAuthorBooksCount(String) : BigInteger {abstract} | ||||||
|  |     + getAuthorByUsername(String) : Author {abstract} | ||||||
|  |     + getAuthorsCount() : BigInteger {abstract} | ||||||
|  |     + getBook(String) : Book {abstract} | ||||||
|  |   } | ||||||
|  |   class QueryServiceImpl { | ||||||
|  |     - sessionFactory : SessionFactory | ||||||
|  |     + QueryServiceImpl() | ||||||
|  |     + getAuthorBooks(username : String) : List<Book> | ||||||
|  |     + getAuthorBooksCount(username : String) : BigInteger | ||||||
|  |     + getAuthorByUsername(username : String) : Author | ||||||
|  |     + getAuthorsCount() : BigInteger | ||||||
|  |     + getBook(title : String) : Book | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | package com.iluwatar.cqrs.domain.model { | ||||||
|  |   class Author { | ||||||
|  |     - email : String | ||||||
|  |     - id : long | ||||||
|  |     - name : String | ||||||
|  |     - username : String | ||||||
|  |     # Author() | ||||||
|  |     + Author(username : String, name : String, email : String) | ||||||
|  |     + getEmail() : String | ||||||
|  |     + getId() : long | ||||||
|  |     + getName() : String | ||||||
|  |     + getUsername() : String | ||||||
|  |     + setEmail(email : String) | ||||||
|  |     + setId(id : long) | ||||||
|  |     + setName(name : String) | ||||||
|  |     + setUsername(username : String) | ||||||
|  |     + toString() : String | ||||||
|  |   } | ||||||
|  |   class Book { | ||||||
|  |     - author : Author | ||||||
|  |     - id : long | ||||||
|  |     - price : double | ||||||
|  |     - title : String | ||||||
|  |     # Book() | ||||||
|  |     + Book(title : String, price : double, author : Author) | ||||||
|  |     + getAuthor() : Author | ||||||
|  |     + getId() : long | ||||||
|  |     + getPrice() : double | ||||||
|  |     + getTitle() : String | ||||||
|  |     + setAuthor(author : Author) | ||||||
|  |     + setId(id : long) | ||||||
|  |     + setPrice(price : double) | ||||||
|  |     + setTitle(title : String) | ||||||
|  |     + toString() : String | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | Book -->  "-author" Author | ||||||
|  | CommandServiceImpl ..|> ICommandService  | ||||||
|  | QueryServiceImpl ..|> IQueryService  | ||||||
|  | @enduml | ||||||
							
								
								
									
										42
									
								
								cqrs/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								cqrs/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | <?xml version="1.0"?> | ||||||
|  | <!-- The MIT License Copyright (c) 2014-2016 Ilkka Seppälä Permission is  | ||||||
|  | 	hereby granted, free of charge, to any person obtaining a copy of this software  | ||||||
|  | 	and associated documentation files (the "Software"), to deal in the Software  | ||||||
|  | 	without restriction, including without limitation the rights to use, copy,  | ||||||
|  | 	modify, merge, publish, distribute, sublicense, and/or sell copies of the  | ||||||
|  | 	Software, and to permit persons to whom the Software is furnished to do so,  | ||||||
|  | 	subject to the following conditions: The above copyright notice and this  | ||||||
|  | 	permission notice shall be included in all copies or substantial portions  | ||||||
|  | 	of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY  | ||||||
|  | 	KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF  | ||||||
|  | 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN  | ||||||
|  | 	NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,  | ||||||
|  | 	DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,  | ||||||
|  | 	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER  | ||||||
|  | 	DEALINGS IN THE SOFTWARE. --> | ||||||
|  | <project | ||||||
|  | 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | ||||||
|  | 	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | ||||||
|  | 	<modelVersion>4.0.0</modelVersion> | ||||||
|  | 	<parent> | ||||||
|  | 		<groupId>com.iluwatar</groupId> | ||||||
|  | 		<artifactId>java-design-patterns</artifactId> | ||||||
|  | 		<version>1.17.0-SNAPSHOT</version> | ||||||
|  | 	</parent> | ||||||
|  | 	<artifactId>cqrs</artifactId> | ||||||
|  | 	<dependencies> | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>junit</groupId> | ||||||
|  | 			<artifactId>junit</artifactId> | ||||||
|  | 			<scope>test</scope> | ||||||
|  | 		</dependency> | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>com.h2database</groupId> | ||||||
|  | 			<artifactId>h2</artifactId> | ||||||
|  | 		</dependency> | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>org.hibernate</groupId> | ||||||
|  | 			<artifactId>hibernate-core</artifactId> | ||||||
|  | 		</dependency> | ||||||
|  | 	</dependencies> | ||||||
|  | </project> | ||||||
							
								
								
									
										95
									
								
								cqrs/src/main/java/com/iluwatar/cqrs/app/App.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								cqrs/src/main/java/com/iluwatar/cqrs/app/App.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | |||||||
|  | /** | ||||||
|  |  * The MIT License | ||||||
|  |  * Copyright (c) 2014-2016 Ilkka Seppälä | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  | package com.iluwatar.cqrs.app; | ||||||
|  |  | ||||||
|  | import java.math.BigInteger; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import com.iluwatar.cqrs.commandes.CommandServiceImpl; | ||||||
|  | import com.iluwatar.cqrs.commandes.ICommandService; | ||||||
|  | import com.iluwatar.cqrs.dto.Author; | ||||||
|  | import com.iluwatar.cqrs.dto.Book; | ||||||
|  | import com.iluwatar.cqrs.queries.IQueryService; | ||||||
|  | import com.iluwatar.cqrs.queries.QueryServiceImpl; | ||||||
|  | import com.iluwatar.cqrs.util.HibernateUtil; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * CQRS : Command Query Responsibility Segregation. A pattern used to separate query services from commands or writes | ||||||
|  |  * services. The pattern is very simple but it has many consequences. For example, it can be used to tackle down a | ||||||
|  |  * complex domain, or to use other architectures that were hard to implement with the classical way. | ||||||
|  |  *  | ||||||
|  |  * This implementation is an example of managing books and authors in a library. The persistence of books and authors is | ||||||
|  |  * done according to the CQRS architecture. A command side that deals with a data model to persist(insert,update,delete) | ||||||
|  |  * objects to a database. And a query side that uses native queries to get data from the database and return objects as | ||||||
|  |  * DTOs (Data transfer Objects). | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | public class App { | ||||||
|  |   private static final Logger LOGGER = LoggerFactory.getLogger(App.class); | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Program entry point | ||||||
|  |    *  | ||||||
|  |    * @param args | ||||||
|  |    *          command line args | ||||||
|  |    */ | ||||||
|  |   public static void main(String[] args) { | ||||||
|  |     ICommandService commands = new CommandServiceImpl(); | ||||||
|  |  | ||||||
|  |     // Create Authors and Books using CommandService | ||||||
|  |     commands.authorCreated("eEvans", "Eric Evans", "eEvans@email.com"); | ||||||
|  |     commands.authorCreated("jBloch", "Joshua Bloch", "jBloch@email.com"); | ||||||
|  |     commands.authorCreated("mFowler", "Martin Fowler", "mFowler@email.com"); | ||||||
|  |  | ||||||
|  |     commands.bookAddedToAuthor("Domain-Driven Design", 60.08, "eEvans"); | ||||||
|  |     commands.bookAddedToAuthor("Effective Java", 40.54, "jBloch"); | ||||||
|  |     commands.bookAddedToAuthor("Java Puzzlers", 39.99, "jBloch"); | ||||||
|  |     commands.bookAddedToAuthor("Java Concurrency in Practice", 29.40, "jBloch"); | ||||||
|  |     commands.bookAddedToAuthor("Patterns of Enterprise Application Architecture", 54.01, "mFowler"); | ||||||
|  |     commands.bookAddedToAuthor("Domain Specific Languages", 48.89, "mFowler"); | ||||||
|  |     commands.authorNameUpdated("eEvans", "Eric J. Evans"); | ||||||
|  |  | ||||||
|  |     IQueryService queries = new QueryServiceImpl(); | ||||||
|  |  | ||||||
|  |     // Query the database using QueryService | ||||||
|  |     Author nullAuthor = queries.getAuthorByUsername("username"); | ||||||
|  |     Author eEvans = queries.getAuthorByUsername("eEvans"); | ||||||
|  |     BigInteger jBlochBooksCount = queries.getAuthorBooksCount("jBloch"); | ||||||
|  |     BigInteger authorsCount = queries.getAuthorsCount(); | ||||||
|  |     Book dddBook = queries.getBook("Domain-Driven Design"); | ||||||
|  |     List<Book> jBlochBooks = queries.getAuthorBooks("jBloch"); | ||||||
|  |  | ||||||
|  |     LOGGER.info("Author username : {}", nullAuthor); | ||||||
|  |     LOGGER.info("Author eEvans : {}", eEvans); | ||||||
|  |     LOGGER.info("jBloch number of books : {}", jBlochBooksCount); | ||||||
|  |     LOGGER.info("Number of authors : {}", authorsCount); | ||||||
|  |     LOGGER.info("DDD book : {}", dddBook); | ||||||
|  |     LOGGER.info("jBloch books : {}", jBlochBooks); | ||||||
|  |  | ||||||
|  |     HibernateUtil.getSessionFactory().close(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,123 @@ | |||||||
|  | package com.iluwatar.cqrs.commandes; | ||||||
|  |  | ||||||
|  | import org.hibernate.Query; | ||||||
|  | import org.hibernate.Session; | ||||||
|  | import org.hibernate.SessionFactory; | ||||||
|  |  | ||||||
|  | import com.iluwatar.cqrs.domain.model.Author; | ||||||
|  | import com.iluwatar.cqrs.domain.model.Book; | ||||||
|  | import com.iluwatar.cqrs.util.HibernateUtil; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This class is an implementation of {@link ICommandService} interface. It uses Hibernate as an api for persistence. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | public class CommandServiceImpl implements ICommandService { | ||||||
|  |  | ||||||
|  |   private SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); | ||||||
|  |  | ||||||
|  |   private Author getAuthorByUsername(String username) { | ||||||
|  |     Author author = null; | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       Query query = session.createQuery("from Author where username=:username"); | ||||||
|  |       query.setParameter("username", username); | ||||||
|  |       author = (Author) query.uniqueResult(); | ||||||
|  |     } | ||||||
|  |     if (author == null) { | ||||||
|  |       HibernateUtil.getSessionFactory().close(); | ||||||
|  |       throw new NullPointerException("Author " + username + " doesn't exist!"); | ||||||
|  |     } | ||||||
|  |     return author; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private Book getBookByTitle(String title) { | ||||||
|  |     Book book = null; | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       Query query = session.createQuery("from Book where title=:title"); | ||||||
|  |       query.setParameter("title", title); | ||||||
|  |       book = (Book) query.uniqueResult(); | ||||||
|  |     } | ||||||
|  |     if (book == null) { | ||||||
|  |       HibernateUtil.getSessionFactory().close(); | ||||||
|  |       throw new NullPointerException("Book " + title + " doesn't exist!"); | ||||||
|  |     } | ||||||
|  |     return book; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public void authorCreated(String username, String name, String email) { | ||||||
|  |     Author author = new Author(username, name, email); | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       session.beginTransaction(); | ||||||
|  |       session.save(author); | ||||||
|  |       session.getTransaction().commit(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public void bookAddedToAuthor(String title, double price, String username) { | ||||||
|  |     Author author = getAuthorByUsername(username); | ||||||
|  |     Book book = new Book(title, price, author); | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       session.beginTransaction(); | ||||||
|  |       session.save(book); | ||||||
|  |       session.getTransaction().commit(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public void authorNameUpdated(String username, String name) { | ||||||
|  |     Author author = getAuthorByUsername(username); | ||||||
|  |     author.setName(name); | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       session.beginTransaction(); | ||||||
|  |       session.update(author); | ||||||
|  |       session.getTransaction().commit(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public void authorUsernameUpdated(String oldUsername, String newUsername) { | ||||||
|  |     Author author = getAuthorByUsername(oldUsername); | ||||||
|  |     author.setUsername(newUsername); | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       session.beginTransaction(); | ||||||
|  |       session.update(author); | ||||||
|  |       session.getTransaction().commit(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public void authorEmailUpdated(String username, String email) { | ||||||
|  |     Author author = getAuthorByUsername(username); | ||||||
|  |     author.setEmail(email); | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       session.beginTransaction(); | ||||||
|  |       session.update(author); | ||||||
|  |       session.getTransaction().commit(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public void bookTitleUpdated(String oldTitle, String newTitle) { | ||||||
|  |     Book book = getBookByTitle(oldTitle); | ||||||
|  |     book.setTitle(newTitle); | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       session.beginTransaction(); | ||||||
|  |       session.update(book); | ||||||
|  |       session.getTransaction().commit(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public void bookPriceUpdated(String title, double price) { | ||||||
|  |     Book book = getBookByTitle(title); | ||||||
|  |     book.setPrice(price); | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       session.beginTransaction(); | ||||||
|  |       session.update(book); | ||||||
|  |       session.getTransaction().commit(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | package com.iluwatar.cqrs.commandes; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This interface represents the commands of the CQRS pattern | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | public interface ICommandService { | ||||||
|  |  | ||||||
|  |   void authorCreated(String username, String name, String email); | ||||||
|  |  | ||||||
|  |   void bookAddedToAuthor(String title, double price, String username); | ||||||
|  |  | ||||||
|  |   void authorNameUpdated(String username, String name); | ||||||
|  |  | ||||||
|  |   void authorUsernameUpdated(String oldUsername, String newUsername); | ||||||
|  |  | ||||||
|  |   void authorEmailUpdated(String username, String email); | ||||||
|  |  | ||||||
|  |   void bookTitleUpdated(String oldTitle, String newTitle); | ||||||
|  |  | ||||||
|  |   void bookPriceUpdated(String title, double price); | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,78 @@ | |||||||
|  | package com.iluwatar.cqrs.domain.model; | ||||||
|  |  | ||||||
|  | import javax.persistence.Entity; | ||||||
|  | import javax.persistence.GeneratedValue; | ||||||
|  | import javax.persistence.GenerationType; | ||||||
|  | import javax.persistence.Id; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This is an Author entity. It is used by Hibernate for persistence. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | @Entity | ||||||
|  | public class Author { | ||||||
|  |   @Id | ||||||
|  |   @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||||
|  |   private long id; | ||||||
|  |   private String username; | ||||||
|  |   private String name; | ||||||
|  |   private String email; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    *  | ||||||
|  |    * @param username | ||||||
|  |    *          username of the author | ||||||
|  |    * @param name | ||||||
|  |    *          name of the author | ||||||
|  |    * @param email | ||||||
|  |    *          email of the author | ||||||
|  |    */ | ||||||
|  |   public Author(String username, String name, String email) { | ||||||
|  |     super(); | ||||||
|  |     this.username = username; | ||||||
|  |     this.name = name; | ||||||
|  |     this.email = email; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected Author() { | ||||||
|  |     super(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public long getId() { | ||||||
|  |     return id; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void setId(long id) { | ||||||
|  |     this.id = id; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public String getUsername() { | ||||||
|  |     return username; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void setUsername(String username) { | ||||||
|  |     this.username = username; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public String getName() { | ||||||
|  |     return name; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void setName(String name) { | ||||||
|  |     this.name = name; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public String getEmail() { | ||||||
|  |     return email; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void setEmail(String email) { | ||||||
|  |     this.email = email; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public String toString() { | ||||||
|  |     return "Author [name=" + name + ", email=" + email + "]"; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										80
									
								
								cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Book.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Book.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | package com.iluwatar.cqrs.domain.model; | ||||||
|  |  | ||||||
|  | import javax.persistence.Entity; | ||||||
|  | import javax.persistence.GeneratedValue; | ||||||
|  | import javax.persistence.GenerationType; | ||||||
|  | import javax.persistence.Id; | ||||||
|  | import javax.persistence.ManyToOne; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This is a Book entity. It is used by Hibernate for persistence. Many books can be written by one {@link Author} | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | @Entity | ||||||
|  | public class Book { | ||||||
|  |   @Id | ||||||
|  |   @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||||
|  |   private long id; | ||||||
|  |   private String title; | ||||||
|  |   private double price; | ||||||
|  |   @ManyToOne | ||||||
|  |   private Author author; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    *  | ||||||
|  |    * @param title | ||||||
|  |    *          title of the book | ||||||
|  |    * @param price | ||||||
|  |    *          price of the book | ||||||
|  |    * @param author | ||||||
|  |    *          author of the book | ||||||
|  |    */ | ||||||
|  |   public Book(String title, double price, Author author) { | ||||||
|  |     super(); | ||||||
|  |     this.title = title; | ||||||
|  |     this.price = price; | ||||||
|  |     this.author = author; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected Book() { | ||||||
|  |     super(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public long getId() { | ||||||
|  |     return id; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void setId(long id) { | ||||||
|  |     this.id = id; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public String getTitle() { | ||||||
|  |     return title; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void setTitle(String title) { | ||||||
|  |     this.title = title; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public double getPrice() { | ||||||
|  |     return price; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void setPrice(double price) { | ||||||
|  |     this.price = price; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public Author getAuthor() { | ||||||
|  |     return author; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public void setAuthor(Author author) { | ||||||
|  |     this.author = author; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public String toString() { | ||||||
|  |     return "Book [title=" + title + ", price=" + price + ", author=" + author + "]"; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										71
									
								
								cqrs/src/main/java/com/iluwatar/cqrs/dto/Author.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								cqrs/src/main/java/com/iluwatar/cqrs/dto/Author.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | |||||||
|  | package com.iluwatar.cqrs.dto; | ||||||
|  |  | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * This is a DTO (Data Transfer Object) author, contains only useful information to be returned | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | public class Author { | ||||||
|  |  | ||||||
|  |   private String name; | ||||||
|  |   private String email; | ||||||
|  |   private String username; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    *  | ||||||
|  |    * @param name | ||||||
|  |    *          name of the author | ||||||
|  |    * @param email | ||||||
|  |    *          email of the author | ||||||
|  |    * @param username | ||||||
|  |    *          username of the author | ||||||
|  |    */ | ||||||
|  |   public Author(String name, String email, String username) { | ||||||
|  |     super(); | ||||||
|  |     this.name = name; | ||||||
|  |     this.email = email; | ||||||
|  |     this.username = username; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public Author() { | ||||||
|  |     super(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public String getName() { | ||||||
|  |     return name; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public String getEmail() { | ||||||
|  |     return email; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public String getUsername() { | ||||||
|  |     return username; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public String toString() { | ||||||
|  |     return "AuthorDTO [name=" + name + ", email=" + email + ", username=" + username + "]"; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public int hashCode() { | ||||||
|  |     return Objects.hash(username, name, email); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public boolean equals(Object obj) { | ||||||
|  |     if (this == obj) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     if (!(obj instanceof Author)) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     Author other = (Author) obj; | ||||||
|  |     return username.equals(other.getUsername()) && email.equals(other.getEmail()) && name.equals(other.getName()); | ||||||
|  |  | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								cqrs/src/main/java/com/iluwatar/cqrs/dto/Book.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								cqrs/src/main/java/com/iluwatar/cqrs/dto/Book.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | package com.iluwatar.cqrs.dto; | ||||||
|  |  | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * This is a DTO (Data Transfer Object) book, contains only useful information to be returned | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | public class Book { | ||||||
|  |  | ||||||
|  |   private String title; | ||||||
|  |   private double price; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    *  | ||||||
|  |    * @param title | ||||||
|  |    *          title of the book | ||||||
|  |    * @param price | ||||||
|  |    *          price of the book | ||||||
|  |    */ | ||||||
|  |   public Book(String title, double price) { | ||||||
|  |     super(); | ||||||
|  |     this.title = title; | ||||||
|  |     this.price = price; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public Book() { | ||||||
|  |     super(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public String getTitle() { | ||||||
|  |     return title; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public double getPrice() { | ||||||
|  |     return price; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public String toString() { | ||||||
|  |     return "BookDTO [title=" + title + ", price=" + price + "]"; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public int hashCode() { | ||||||
|  |     return Objects.hash(title, price); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public boolean equals(Object obj) { | ||||||
|  |     if (this == obj) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     if (!(obj instanceof Book)) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     Book book = (Book) obj; | ||||||
|  |     return title.equals(book.getTitle()) && price == book.getPrice(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | package com.iluwatar.cqrs.queries; | ||||||
|  |  | ||||||
|  | import java.math.BigInteger; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import com.iluwatar.cqrs.dto.Author; | ||||||
|  | import com.iluwatar.cqrs.dto.Book; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * This interface represents the query methods of the CQRS pattern | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | public interface IQueryService { | ||||||
|  |  | ||||||
|  |   Author getAuthorByUsername(String username); | ||||||
|  |  | ||||||
|  |   Book getBook(String title); | ||||||
|  |  | ||||||
|  |   List<Book> getAuthorBooks(String username); | ||||||
|  |  | ||||||
|  |   BigInteger getAuthorBooksCount(String username); | ||||||
|  |  | ||||||
|  |   BigInteger getAuthorsCount(); | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,83 @@ | |||||||
|  | package com.iluwatar.cqrs.queries; | ||||||
|  |  | ||||||
|  | import java.math.BigInteger; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import org.hibernate.SQLQuery; | ||||||
|  | import org.hibernate.Session; | ||||||
|  | import org.hibernate.SessionFactory; | ||||||
|  | import org.hibernate.transform.Transformers; | ||||||
|  |  | ||||||
|  | import com.iluwatar.cqrs.dto.Author; | ||||||
|  | import com.iluwatar.cqrs.dto.Book; | ||||||
|  | import com.iluwatar.cqrs.util.HibernateUtil; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This class is an implementation of {@link IQueryService}. It uses Hibernate native queries to return DTOs from the | ||||||
|  |  * database. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | public class QueryServiceImpl implements IQueryService { | ||||||
|  |  | ||||||
|  |   private SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public Author getAuthorByUsername(String username) { | ||||||
|  |     Author authorDTo = null; | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       SQLQuery sqlQuery = session | ||||||
|  |           .createSQLQuery("SELECT a.username as \"username\", a.name as \"name\", a.email as \"email\"" | ||||||
|  |               + "FROM Author a where a.username=:username"); | ||||||
|  |       sqlQuery.setParameter("username", username); | ||||||
|  |       authorDTo = (Author) sqlQuery.setResultTransformer(Transformers.aliasToBean(Author.class)).uniqueResult(); | ||||||
|  |     } | ||||||
|  |     return authorDTo; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public Book getBook(String title) { | ||||||
|  |     Book bookDTo = null; | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       SQLQuery sqlQuery = session | ||||||
|  |           .createSQLQuery("SELECT b.title as \"title\", b.price as \"price\"" + " FROM Book b where b.title=:title"); | ||||||
|  |       sqlQuery.setParameter("title", title); | ||||||
|  |       bookDTo = (Book) sqlQuery.setResultTransformer(Transformers.aliasToBean(Book.class)).uniqueResult(); | ||||||
|  |     } | ||||||
|  |     return bookDTo; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public List<Book> getAuthorBooks(String username) { | ||||||
|  |     List<Book> bookDTos = null; | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       SQLQuery sqlQuery = session.createSQLQuery("SELECT b.title as \"title\", b.price as \"price\"" | ||||||
|  |           + " FROM Author a , Book b where b.author_id = a.id and a.username=:username"); | ||||||
|  |       sqlQuery.setParameter("username", username); | ||||||
|  |       bookDTos = sqlQuery.setResultTransformer(Transformers.aliasToBean(Book.class)).list(); | ||||||
|  |     } | ||||||
|  |     return bookDTos; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public BigInteger getAuthorBooksCount(String username) { | ||||||
|  |     BigInteger bookcount = null; | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       SQLQuery sqlQuery = session.createSQLQuery( | ||||||
|  |           "SELECT count(b.title)" + " FROM  Book b, Author a where b.author_id = a.id and a.username=:username"); | ||||||
|  |       sqlQuery.setParameter("username", username); | ||||||
|  |       bookcount = (BigInteger) sqlQuery.uniqueResult(); | ||||||
|  |     } | ||||||
|  |     return bookcount; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Override | ||||||
|  |   public BigInteger getAuthorsCount() { | ||||||
|  |     BigInteger authorcount = null; | ||||||
|  |     try (Session session = sessionFactory.openSession()) { | ||||||
|  |       SQLQuery sqlQuery = session.createSQLQuery("SELECT count(id) from Author"); | ||||||
|  |       authorcount = (BigInteger) sqlQuery.uniqueResult(); | ||||||
|  |     } | ||||||
|  |     return authorcount; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								cqrs/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								cqrs/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | package com.iluwatar.cqrs.util; | ||||||
|  |  | ||||||
|  | import org.hibernate.SessionFactory; | ||||||
|  | import org.hibernate.boot.MetadataSources; | ||||||
|  | import org.hibernate.boot.registry.StandardServiceRegistry; | ||||||
|  | import org.hibernate.boot.registry.StandardServiceRegistryBuilder; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This class simply returns one instance of {@link SessionFactory} initialized when the application is started | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | public class HibernateUtil { | ||||||
|  |  | ||||||
|  |   private static final SessionFactory SESSIONFACTORY = buildSessionFactory(); | ||||||
|  |   private static final Logger LOGGER = LoggerFactory.getLogger(HibernateUtil.class); | ||||||
|  |  | ||||||
|  |   private static SessionFactory buildSessionFactory() { | ||||||
|  |  | ||||||
|  |     // configures settings from hibernate.cfg.xml | ||||||
|  |     final StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure().build(); | ||||||
|  |     try { | ||||||
|  |       return new MetadataSources(registry).buildMetadata().buildSessionFactory(); | ||||||
|  |     } catch (Exception ex) { | ||||||
|  |       StandardServiceRegistryBuilder.destroy(registry); | ||||||
|  |       LOGGER.error("Initial SessionFactory creation failed." + ex); | ||||||
|  |       throw new ExceptionInInitializerError(ex); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public static SessionFactory getSessionFactory() { | ||||||
|  |     return SESSIONFACTORY; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								cqrs/src/main/resources/hibernate.cfg.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cqrs/src/main/resources/hibernate.cfg.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <!DOCTYPE hibernate-configuration SYSTEM | ||||||
|  | "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> | ||||||
|  |  | ||||||
|  | <hibernate-configuration> | ||||||
|  |     <session-factory> | ||||||
|  |         <property name="dialect">org.hibernate.dialect.H2Dialect</property> | ||||||
|  |         <property name="connection.driver_class">org.h2.Driver</property> | ||||||
|  |         <property name="connection.url">jdbc:h2:mem:test</property> | ||||||
|  |         <property name="connection.username">sa</property> | ||||||
|  |         <property name="hbm2ddl.auto">create</property> | ||||||
|  |         <mapping class="com.iluwatar.cqrs.domain.model.Author" /> | ||||||
|  |         <mapping class="com.iluwatar.cqrs.domain.model.Book" /> | ||||||
|  |     </session-factory> | ||||||
|  | </hibernate-configuration> | ||||||
							
								
								
									
										13
									
								
								cqrs/src/main/resources/logback.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								cqrs/src/main/resources/logback.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <configuration> | ||||||
|  |     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||||||
|  |         <encoder> | ||||||
|  |             <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n | ||||||
|  |             </pattern> | ||||||
|  |         </encoder> | ||||||
|  |     </appender> | ||||||
|  |  | ||||||
|  |     <root level="info"> | ||||||
|  |         <appender-ref ref="STDOUT" /> | ||||||
|  |     </root> | ||||||
|  | </configuration> | ||||||
							
								
								
									
										98
									
								
								cqrs/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								cqrs/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | |||||||
|  | package com.iluwatar.cqrs; | ||||||
|  |  | ||||||
|  | import static org.junit.Assert.assertEquals; | ||||||
|  | import static org.junit.Assert.assertTrue; | ||||||
|  |  | ||||||
|  | import java.math.BigInteger; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import org.junit.BeforeClass; | ||||||
|  | import org.junit.Test; | ||||||
|  |  | ||||||
|  | import com.iluwatar.cqrs.commandes.CommandServiceImpl; | ||||||
|  | import com.iluwatar.cqrs.commandes.ICommandService; | ||||||
|  | import com.iluwatar.cqrs.dto.Author; | ||||||
|  | import com.iluwatar.cqrs.dto.Book; | ||||||
|  | import com.iluwatar.cqrs.queries.IQueryService; | ||||||
|  | import com.iluwatar.cqrs.queries.QueryServiceImpl; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Integration test of IQueryService and ICommandService with h2 data | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | public class IntegrationTest { | ||||||
|  |  | ||||||
|  |   private static IQueryService queryService; | ||||||
|  |   private static ICommandService commandService; | ||||||
|  |  | ||||||
|  |   @BeforeClass | ||||||
|  |   public static void initialize() { | ||||||
|  |     commandService = new CommandServiceImpl(); | ||||||
|  |     queryService = new QueryServiceImpl(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @BeforeClass | ||||||
|  |   public static void populateDatabase() { | ||||||
|  |     // create first author1 | ||||||
|  |     commandService.authorCreated("username1", "name1", "email1"); | ||||||
|  |  | ||||||
|  |     // create author1 and update all its data | ||||||
|  |     commandService.authorCreated("username2", "name2", "email2"); | ||||||
|  |     commandService.authorEmailUpdated("username2", "new_email2"); | ||||||
|  |     commandService.authorNameUpdated("username2", "new_name2"); | ||||||
|  |     commandService.authorUsernameUpdated("username2", "new_username2"); | ||||||
|  |  | ||||||
|  |     // add book1 to author1 | ||||||
|  |     commandService.bookAddedToAuthor("title1", 10, "username1"); | ||||||
|  |  | ||||||
|  |     // add book2 to author1 and update all its data | ||||||
|  |     commandService.bookAddedToAuthor("title2", 20, "username1"); | ||||||
|  |     commandService.bookPriceUpdated("title2", 30); | ||||||
|  |     commandService.bookTitleUpdated("title2", "new_title2"); | ||||||
|  |  | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Test | ||||||
|  |   public void testGetAuthorByUsername() { | ||||||
|  |     Author author = queryService.getAuthorByUsername("username1"); | ||||||
|  |     assertEquals("username1", author.getUsername()); | ||||||
|  |     assertEquals("name1", author.getName()); | ||||||
|  |     assertEquals("email1", author.getEmail()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Test | ||||||
|  |   public void testGetUpdatedAuthorByUsername() { | ||||||
|  |     Author author = queryService.getAuthorByUsername("new_username2"); | ||||||
|  |     Author expectedAuthor = new Author("new_name2", "new_email2", "new_username2"); | ||||||
|  |     assertEquals(expectedAuthor, author); | ||||||
|  |  | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Test | ||||||
|  |   public void testGetBook() { | ||||||
|  |     Book book = queryService.getBook("title1"); | ||||||
|  |     assertEquals("title1", book.getTitle()); | ||||||
|  |     assertEquals(10, book.getPrice(), 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Test | ||||||
|  |   public void testGetAuthorBooks() { | ||||||
|  |     List<Book> books = queryService.getAuthorBooks("username1"); | ||||||
|  |     assertTrue(books.size() == 2); | ||||||
|  |     assertTrue(books.contains(new Book("title1", 10))); | ||||||
|  |     assertTrue(books.contains(new Book("new_title2", 30))); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Test | ||||||
|  |   public void testGetAuthorBooksCount() { | ||||||
|  |     BigInteger bookCount = queryService.getAuthorBooksCount("username1"); | ||||||
|  |     assertEquals(new BigInteger("2"), bookCount); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Test | ||||||
|  |   public void testGetAuthorsCount() { | ||||||
|  |     BigInteger authorCount = queryService.getAuthorsCount(); | ||||||
|  |     assertEquals(new BigInteger("2"), authorCount); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								cqrs/src/test/resources/hibernate.cfg.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								cqrs/src/test/resources/hibernate.cfg.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <!DOCTYPE hibernate-configuration SYSTEM | ||||||
|  | "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> | ||||||
|  |  | ||||||
|  | <hibernate-configuration> | ||||||
|  |     <session-factory> | ||||||
|  |         <property name="dialect">org.hibernate.dialect.H2Dialect</property> | ||||||
|  |         <property name="connection.driver_class">org.h2.Driver</property> | ||||||
|  |         <property name="connection.url">jdbc:h2:mem:test</property> | ||||||
|  |         <property name="connection.username">sa</property> | ||||||
|  |         <property name="hbm2ddl.auto">create</property> | ||||||
|  |         <mapping class="com.iluwatar.cqrs.domain.model.Author" /> | ||||||
|  |         <mapping class="com.iluwatar.cqrs.domain.model.Book" /> | ||||||
|  |     </session-factory> | ||||||
|  | </hibernate-configuration> | ||||||
							
								
								
									
										13
									
								
								cqrs/src/test/resources/logback.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								cqrs/src/test/resources/logback.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <configuration> | ||||||
|  |     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||||||
|  |         <encoder> | ||||||
|  |             <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n | ||||||
|  |             </pattern> | ||||||
|  |         </encoder> | ||||||
|  |     </appender> | ||||||
|  |  | ||||||
|  |     <root level="info"> | ||||||
|  |         <appender-ref ref="STDOUT" /> | ||||||
|  |     </root> | ||||||
|  | </configuration> | ||||||
		Reference in New Issue
	
	Block a user