diff --git a/cqrs/README.md b/cqrs/README.md
new file mode 100644
index 000000000..3cdd429a1
--- /dev/null
+++ b/cqrs/README.md
@@ -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)
diff --git a/cqrs/etc/cqrs.png b/cqrs/etc/cqrs.png
new file mode 100644
index 000000000..28174bc9a
Binary files /dev/null and b/cqrs/etc/cqrs.png differ
diff --git a/cqrs/etc/cqrs.ucls b/cqrs/etc/cqrs.ucls
new file mode 100644
index 000000000..6cdfebbb2
--- /dev/null
+++ b/cqrs/etc/cqrs.ucls
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cqrs/etc/cqrs.urm.puml b/cqrs/etc/cqrs.urm.puml
new file mode 100644
index 000000000..6dd65dd3a
--- /dev/null
+++ b/cqrs/etc/cqrs.urm.puml
@@ -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 {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
+ + 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
\ No newline at end of file
diff --git a/cqrs/pom.xml b/cqrs/pom.xml
new file mode 100644
index 000000000..6c036a933
--- /dev/null
+++ b/cqrs/pom.xml
@@ -0,0 +1,42 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.17.0-SNAPSHOT
+
+ cqrs
+
+
+ junit
+ junit
+ test
+
+
+ com.h2database
+ h2
+
+
+ org.hibernate
+ hibernate-core
+
+
+
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/app/App.java b/cqrs/src/main/java/com/iluwatar/cqrs/app/App.java
new file mode 100644
index 000000000..a943a7f88
--- /dev/null
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/app/App.java
@@ -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 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();
+ }
+
+}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java b/cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
new file mode 100644
index 000000000..86d9cb10b
--- /dev/null
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
@@ -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();
+ }
+ }
+
+}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/commandes/ICommandService.java b/cqrs/src/main/java/com/iluwatar/cqrs/commandes/ICommandService.java
new file mode 100644
index 000000000..eb3cc43a1
--- /dev/null
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/commandes/ICommandService.java
@@ -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);
+
+}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Author.java b/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Author.java
new file mode 100644
index 000000000..308fb4f5d
--- /dev/null
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Author.java
@@ -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 + "]";
+ }
+
+}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Book.java b/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Book.java
new file mode 100644
index 000000000..3d14fae47
--- /dev/null
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Book.java
@@ -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 + "]";
+ }
+
+}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/dto/Author.java b/cqrs/src/main/java/com/iluwatar/cqrs/dto/Author.java
new file mode 100644
index 000000000..b7a7ae880
--- /dev/null
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/dto/Author.java
@@ -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());
+
+ }
+
+}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/dto/Book.java b/cqrs/src/main/java/com/iluwatar/cqrs/dto/Book.java
new file mode 100644
index 000000000..b3f0f62d3
--- /dev/null
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/dto/Book.java
@@ -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();
+ }
+
+}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/queries/IQueryService.java b/cqrs/src/main/java/com/iluwatar/cqrs/queries/IQueryService.java
new file mode 100644
index 000000000..3e3d6ab10
--- /dev/null
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/queries/IQueryService.java
@@ -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 getAuthorBooks(String username);
+
+ BigInteger getAuthorBooksCount(String username);
+
+ BigInteger getAuthorsCount();
+
+}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java b/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
new file mode 100644
index 000000000..f3a616d59
--- /dev/null
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
@@ -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 getAuthorBooks(String username) {
+ List 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;
+ }
+
+}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java b/cqrs/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java
new file mode 100644
index 000000000..c8f41762e
--- /dev/null
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java
@@ -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;
+ }
+
+}
diff --git a/cqrs/src/main/resources/hibernate.cfg.xml b/cqrs/src/main/resources/hibernate.cfg.xml
new file mode 100644
index 000000000..151983337
--- /dev/null
+++ b/cqrs/src/main/resources/hibernate.cfg.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ org.hibernate.dialect.H2Dialect
+ org.h2.Driver
+ jdbc:h2:mem:test
+ sa
+ create
+
+
+
+
\ No newline at end of file
diff --git a/cqrs/src/main/resources/logback.xml b/cqrs/src/main/resources/logback.xml
new file mode 100644
index 000000000..6b5d24345
--- /dev/null
+++ b/cqrs/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
diff --git a/cqrs/src/test/java/com/iluwatar/cqrs/IntegrationTest.java b/cqrs/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
new file mode 100644
index 000000000..c9e3b36f4
--- /dev/null
+++ b/cqrs/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
@@ -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 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);
+ }
+
+}
diff --git a/cqrs/src/test/resources/hibernate.cfg.xml b/cqrs/src/test/resources/hibernate.cfg.xml
new file mode 100644
index 000000000..151983337
--- /dev/null
+++ b/cqrs/src/test/resources/hibernate.cfg.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ org.hibernate.dialect.H2Dialect
+ org.h2.Driver
+ jdbc:h2:mem:test
+ sa
+ create
+
+
+
+
\ No newline at end of file
diff --git a/cqrs/src/test/resources/logback.xml b/cqrs/src/test/resources/logback.xml
new file mode 100644
index 000000000..6b5d24345
--- /dev/null
+++ b/cqrs/src/test/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 3653a6fe0..4f4b5dee6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -142,6 +142,7 @@
balking
extension-objects
marker
+ cqrs