Repository: mp911de/CleanArchitecture Branch: main Commit: b180299ad87f Files: 69 Total size: 93.3 KB Directory structure: gitextract_5gr77y1v/ ├── .gitignore ├── README.md ├── application-model/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── biz/ │ └── paluch/ │ └── clean/ │ └── architecture/ │ └── applicationmodel/ │ ├── AbstractModel.java │ ├── Item.java │ ├── NotFoundException.java │ ├── Order.java │ ├── OrderItem.java │ └── User.java ├── commons/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── biz/ │ └── paluch/ │ └── clean/ │ └── architecture/ │ └── commons/ │ ├── DateProvider.java │ └── StaticDateProvider.java ├── contracts/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── biz/ │ └── paluch/ │ └── clean/ │ └── architecture/ │ └── contracts/ │ ├── repositories/ │ │ ├── ItemRepository.java │ │ ├── OrderRepository.java │ │ └── UserRepository.java │ └── usecases/ │ ├── CreateOrUpdateItem.java │ ├── ListItems.java │ ├── ListItemsOutput.java │ ├── PlaceOrder.java │ ├── PlaceOrderOutput.java │ └── PlaceOrderRequest.java ├── delivery/ │ └── web/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── biz/ │ │ └── paluch/ │ │ └── clean/ │ │ └── architecture/ │ │ ├── di_example/ │ │ │ └── PlaceOrderWithDependencies.java │ │ ├── facade/ │ │ │ ├── ItemService.java │ │ │ └── OrderService.java │ │ ├── frontend/ │ │ │ ├── jsf/ │ │ │ │ ├── ItemController.java │ │ │ │ ├── ItemModel.java │ │ │ │ ├── OrderController.java │ │ │ │ ├── OrderItem.java │ │ │ │ └── OrderModel.java │ │ │ └── rest/ │ │ │ ├── ItemResource.java │ │ │ ├── ItemsRepresentation.java │ │ │ ├── JaxRsActivator.java │ │ │ ├── OrderResource.java │ │ │ └── OrdersRepresentation.java │ │ └── util/ │ │ └── Resources.java │ ├── resources/ │ │ └── META-INF/ │ │ └── persistence.xml │ └── webapp/ │ ├── WEB-INF/ │ │ ├── beans.xml │ │ ├── clean-architecture-ds.xml │ │ ├── faces-config.xml │ │ └── web.xml │ ├── index.xhtml │ ├── items.xhtml │ └── resources/ │ └── css/ │ └── screen.css ├── external/ │ └── jpa-repository/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── biz/ │ │ │ └── paluch/ │ │ │ └── clean/ │ │ │ └── architecture/ │ │ │ └── external/ │ │ │ └── jpa/ │ │ │ ├── entity/ │ │ │ │ ├── ItemEntity.java │ │ │ │ ├── OrderEntity.java │ │ │ │ ├── OrderItemEntity.java │ │ │ │ └── UserEntity.java │ │ │ └── repository/ │ │ │ ├── JpaItemRepository.java │ │ │ ├── JpaOrderRepository.java │ │ │ └── JpaUserRepository.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── beans.xml │ └── test/ │ ├── java/ │ │ └── biz/ │ │ └── paluch/ │ │ └── clean/ │ │ └── architecture/ │ │ └── external/ │ │ └── jpa/ │ │ ├── AbstractJpaTest.java │ │ └── JpaOrderRepositoryTest.java │ └── resources/ │ └── META-INF/ │ └── persistence.xml ├── pom.xml └── use-cases/ ├── pom.xml └── src/ ├── main/ │ ├── java/ │ │ └── biz/ │ │ └── paluch/ │ │ └── clean/ │ │ └── architecture/ │ │ └── usecases/ │ │ ├── advanced/ │ │ │ ├── CreateOrUpdateItemImpl.java │ │ │ ├── ListItemsImpl.java │ │ │ └── PlaceOrderImpl.java │ │ └── simple/ │ │ ├── CreateOrUpdateUser.java │ │ ├── ListOrders.java │ │ └── ValidateOrder.java │ └── resources/ │ └── META-INF/ │ └── beans.xml └── test/ └── java/ └── biz/ └── paluch/ └── clean/ └── architecture/ └── usecases/ ├── CreateOrUpdateUserTest.java ├── ListOrdersTest.java └── PlaceOrderImplTest.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .idea/ target *.iml atlassian-ide-plugin.xml ================================================ FILE: README.md ================================================ Sampler for Clean Architecture/Onion-Architecture [![Build Status](https://snap-ci.com/mp911de/CleanArchitecture/branch/master/build_image)](https://snap-ci.com/mp911de/CleanArchitecture/branch/master) ======================== Author: Mark Paluch
Technologies: CDI, JSF, JPA, EJB, JPA, JAX-RS
Summary: Example Application built using an Onion-Architecture that incorporates multiple technologies
Source:
More Information:
* https://www.paluch.biz/blog/80-clean-your-architecture-databases-the-web-and-service-interfaces-are-just-plugins.html * https://www.paluch.biz/blog/83-clean-architecture-code-examples-for-an-onion-architecture.html What is it? ----------- This simple application consists of a few use cases. The purpose of the application is to show how to apply clean architecture patterns in a Multi-Module Maven/Java environment. It all starts with the data structures/entities/application model. These models are independent of business logic and delivery mechanisms. The models are specific to your domain, but not necessary specific to your application. They live within the `application-model` module. Business rules and use cases, the specific things your application does, reside within the `use-cases` module. They depend on the `application-model` and perhaps on external things that are represented by boundaries, located in `contracts`. Those boundaries are an agreement between the use case and the other side that provides a specific implementation. The `contracts` depend only on the `application-model`. No ORM entities or external-specific API/entities. ORM, caching implementations, clients to external services implement a contract that is located in `external` and its sub-modules. All parts are tied together by the delivery mechanism that integrates the externals and connects the use cases by supplying dependencies to come the system to life. If you need a different implementation for any external, so you can easily change that specific part without affecting other parts of the system. These patterns are verified by real life projects. A word on Clean Architecture ---------------------------- As soon as you dig into the code, you'll notice comments on the one or other class. Subject of these comments is to help to understand the structure and the different styles, which are possible. You'll notice soon, there are many different styles and ways to approach the Clean Architecture style. There are use cases which are built much more simple, e. g. without input/output boundaries and direct usage of dependency injection and there are use-cases which implement input boundaries and use output boundaries. In the end it's up to you, how much you're willing to invest in your architecture. This is, however, only a variety of examples to give you an impression, how to express Clean Architecture with Java. What does it? --------- The use cases are: * CreateOrUpdateItem * CreateOrUpdateUser * ListItems * ListOrders * PlaceOrder * PlaceOrderValidator and a few business entities: * User * OrderItem * Order * Item These use cases can be accessed by REST or Web UI (JSF) and are persisted using JPA within an in-memory H2 Database (everything you need is included). Requirements to run the App ------------------- All you need to build this project is Java 6.0 (Java SDK 1.6) or better, Maven 3.0 or better. The application this project produces is designed to be run on JBoss AS7, WildFly 8 or better. You could easily change the delivery mechanism to a console application with only providing a new delivery mechanism and a different approach how to wire the dependencies. Build and Deploy the Quickstart ------------------------- _NOTE: The following build command assumes you have configured your Maven user settings. If you have not, you must include Maven setting arguments on the command line._ 1. Open a command line and navigate to the root directory of this project. 2. Type this command to build and deploy the archive: mvn clean package wildfly:run 3. This will start a WildFly 10 instance and deploy `target/clean-architecture.war` to the newly started instance. Access the application ------------------------- The application will be running at the following URL: . ================================================ FILE: application-model/pom.xml ================================================ 4.0.0 biz.paluch.clean.architecture clean-architecture 1.0-SNAPSHOT application-model org.apache.commons commons-lang3 ================================================ FILE: application-model/src/main/java/biz/paluch/clean/architecture/applicationmodel/AbstractModel.java ================================================ package biz.paluch.clean.architecture.applicationmodel; import java.io.Serializable; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; /** * @author Mark Paluch * @since 07.02.14 07:34 */ public abstract class AbstractModel implements Serializable { @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this, false); } @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj, false); } @Override public String toString() { return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE); } } ================================================ FILE: application-model/src/main/java/biz/paluch/clean/architecture/applicationmodel/Item.java ================================================ package biz.paluch.clean.architecture.applicationmodel; /** * @author Mark Paluch * @since 01.08.13 07:51 */ public class Item extends AbstractModel { private String item; public String getItem() { return item; } public void setItem(String item) { this.item = item; } } ================================================ FILE: application-model/src/main/java/biz/paluch/clean/architecture/applicationmodel/NotFoundException.java ================================================ package biz.paluch.clean.architecture.applicationmodel; /** * @author Mark Paluch * @since 01.08.13 07:47 */ public class NotFoundException extends RuntimeException { public NotFoundException(String message) { super(message); } } ================================================ FILE: application-model/src/main/java/biz/paluch/clean/architecture/applicationmodel/Order.java ================================================ package biz.paluch.clean.architecture.applicationmodel; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @author Mark Paluch * @since 01.08.13 07:23 */ public class Order extends AbstractModel { private String orderId; private Date orderDate; private List items = new ArrayList<>(); private User createdBy; public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public Date getOrderDate() { return orderDate; } public void setOrderDate(Date orderDate) { this.orderDate = orderDate; } public List getItems() { return items; } public User getCreatedBy() { return createdBy; } public void setCreatedBy(User createdBy) { this.createdBy = createdBy; } } ================================================ FILE: application-model/src/main/java/biz/paluch/clean/architecture/applicationmodel/OrderItem.java ================================================ package biz.paluch.clean.architecture.applicationmodel; /** * @author Mark Paluch * @since 01.08.13 07:24 */ public class OrderItem extends AbstractModel { private String orderItem; public OrderItem() { } public OrderItem(String orderItem) { this.orderItem = orderItem; } public String getOrderItem() { return orderItem; } public void setOrderItem(String orderItem) { this.orderItem = orderItem; } } ================================================ FILE: application-model/src/main/java/biz/paluch/clean/architecture/applicationmodel/User.java ================================================ package biz.paluch.clean.architecture.applicationmodel; /** * @author Mark Paluch * @since 01.08.13 07:24 */ public class User extends AbstractModel { private String userName; public User() { } public User(String userName) { this.userName = userName; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } } ================================================ FILE: commons/pom.xml ================================================ 4.0.0 biz.paluch.clean.architecture clean-architecture 1.0-SNAPSHOT commons ================================================ FILE: commons/src/main/java/biz/paluch/clean/architecture/commons/DateProvider.java ================================================ package biz.paluch.clean.architecture.commons; import java.util.Date; /** * @author Mark Paluch * @since 01.08.13 08:01 */ public class DateProvider { private static DateProvider dateProvider = new DateProvider(); public static Date get() { return dateProvider.getCurrentDate(); } public Date getCurrentDate() { return new Date(); } public static void setDateProvider(DateProvider dateProvider) { DateProvider.dateProvider = dateProvider; } } ================================================ FILE: commons/src/main/java/biz/paluch/clean/architecture/commons/StaticDateProvider.java ================================================ package biz.paluch.clean.architecture.commons; import java.util.Date; /** * @author Mark Paluch * @since 01.08.13 08:03 */ public class StaticDateProvider extends DateProvider { private Date currentDate; public static void initialize(Date currentDate) { StaticDateProvider instance = new StaticDateProvider(); instance.setCurrentDate(currentDate); DateProvider.setDateProvider(instance); } @Override public Date getCurrentDate() { return currentDate; } public void setCurrentDate(Date currentDate) { this.currentDate = currentDate; } } ================================================ FILE: contracts/pom.xml ================================================ 4.0.0 biz.paluch.clean.architecture clean-architecture 1.0-SNAPSHOT contracts biz.paluch.clean.architecture application-model ================================================ FILE: contracts/src/main/java/biz/paluch/clean/architecture/contracts/repositories/ItemRepository.java ================================================ package biz.paluch.clean.architecture.contracts.repositories; import java.util.List; import biz.paluch.clean.architecture.applicationmodel.Item; /** * @author Mark Paluch * @since 01.08.13 07:29 */ public interface ItemRepository { Item find(String item); List findAll(); void persist(Item item); } ================================================ FILE: contracts/src/main/java/biz/paluch/clean/architecture/contracts/repositories/OrderRepository.java ================================================ package biz.paluch.clean.architecture.contracts.repositories; import java.util.List; import biz.paluch.clean.architecture.applicationmodel.Order; /** * @author Mark Paluch * @since 01.08.13 07:26 */ public interface OrderRepository { int getNextOrderId(); void persist(Order order); List findOrders(); } ================================================ FILE: contracts/src/main/java/biz/paluch/clean/architecture/contracts/repositories/UserRepository.java ================================================ package biz.paluch.clean.architecture.contracts.repositories; import biz.paluch.clean.architecture.applicationmodel.User; /** * @author Mark Paluch * @since 01.08.13 07:27 */ public interface UserRepository { User find(String userName); void store(User user); } ================================================ FILE: contracts/src/main/java/biz/paluch/clean/architecture/contracts/usecases/CreateOrUpdateItem.java ================================================ package biz.paluch.clean.architecture.contracts.usecases; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; /** * Create or update item Use-Case. * * @author Mark Paluch */ public interface CreateOrUpdateItem { /** * Creates a new or updates an existing item. * * @param item */ void createOrUpdateItem(String item); /** * Setter to {@link ItemRepository} dependency. * * @param itemRepository */ void setItemRepository(ItemRepository itemRepository); } ================================================ FILE: contracts/src/main/java/biz/paluch/clean/architecture/contracts/usecases/ListItems.java ================================================ package biz.paluch.clean.architecture.contracts.usecases; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; /** * List items Use-Case. * * @author Mark Paluch */ public interface ListItems { /** * List all items and pass these to the {@link ListItemsOutput} output. * * @param listItemsOutput */ void listItems(ListItemsOutput listItemsOutput); /** * Setter to {@link ItemRepository} dependency. * * @param itemRepository */ void setItemRepository(ItemRepository itemRepository); } ================================================ FILE: contracts/src/main/java/biz/paluch/clean/architecture/contracts/usecases/ListItemsOutput.java ================================================ package biz.paluch.clean.architecture.contracts.usecases; import java.util.List; import biz.paluch.clean.architecture.applicationmodel.Item; /** * @author Mark Paluch */ public interface ListItemsOutput { void onResponse(List items); } ================================================ FILE: contracts/src/main/java/biz/paluch/clean/architecture/contracts/usecases/PlaceOrder.java ================================================ package biz.paluch.clean.architecture.contracts.usecases; import biz.paluch.clean.architecture.applicationmodel.NotFoundException; /** * This use-case input boundary does not define any dependencies. The {@link #placeOrder(PlaceOrderRequest, PlaceOrderOutput)} * method accept a request data structure and an output boundary for returning the result of the operation. * * @author Mark Paluch */ public interface PlaceOrder { /** * Places an order. * * @param request * @param output */ void placeOrder(PlaceOrderRequest request, PlaceOrderOutput output) throws NotFoundException; } ================================================ FILE: contracts/src/main/java/biz/paluch/clean/architecture/contracts/usecases/PlaceOrderOutput.java ================================================ package biz.paluch.clean.architecture.contracts.usecases; /** * @author Mark Paluch */ public interface PlaceOrderOutput { void onResponse(String orderId); } ================================================ FILE: contracts/src/main/java/biz/paluch/clean/architecture/contracts/usecases/PlaceOrderRequest.java ================================================ package biz.paluch.clean.architecture.contracts.usecases; import java.util.List; /** * @author Mark Paluch */ public class PlaceOrderRequest { public List items; public String userName; } ================================================ FILE: delivery/web/pom.xml ================================================ 4.0.0 biz.paluch.clean.architecture clean-architecture 1.0-SNAPSHOT ../.. web war biz.paluch.clean.architecture contracts biz.paluch.clean.architecture use-cases biz.paluch.clean.architecture application-model biz.paluch.clean.architecture jpa-repository javax.enterprise cdi-api provided org.jboss.spec.javax.annotation jboss-annotations-api_1.1_spec provided org.jboss.spec.javax.ws.rs jboss-jaxrs-api_1.1_spec provided org.jboss.spec.javax.ejb jboss-ejb-api_3.1_spec provided org.jboss.spec.javax.faces jboss-jsf-api_2.1_spec provided org.hibernate.javax.persistence hibernate-jpa-2.0-api provided junit junit test org.mockito mockito-core test org.jboss.resteasy resteasy-jaxrs provided org.jboss.resteasy resteasy-jaxb-provider provided ${project.artifactId} maven-war-plugin ${version.war.plugin} false clean-architecture org.wildfly.plugins wildfly-maven-plugin false clean-architecture.war openshift maven-war-plugin ${version.war.plugin} deployments ROOT ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/di_example/PlaceOrderWithDependencies.java ================================================ package biz.paluch.clean.architecture.di_example; import javax.inject.Inject; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; import biz.paluch.clean.architecture.contracts.repositories.OrderRepository; import biz.paluch.clean.architecture.contracts.repositories.UserRepository; import biz.paluch.clean.architecture.usecases.advanced.PlaceOrderImpl; /** * This Use-Case wrapper extends the {@link PlaceOrderImpl} use case class in order to use declarative dependency injection. * * @author Mark Paluch */ public class PlaceOrderWithDependencies extends PlaceOrderImpl { @Inject @Override public void setOrderRepository(OrderRepository orderRepository) { super.setOrderRepository(orderRepository); } @Inject @Override public void setItemRepository(ItemRepository itemRepository) { super.setItemRepository(itemRepository); } @Inject @Override public void setUserRepository(UserRepository userRepository) { super.setUserRepository(userRepository); } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/facade/ItemService.java ================================================ package biz.paluch.clean.architecture.facade; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.ejb.Stateless; import javax.inject.Inject; import biz.paluch.clean.architecture.applicationmodel.Item; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; import biz.paluch.clean.architecture.contracts.usecases.CreateOrUpdateItem; import biz.paluch.clean.architecture.contracts.usecases.ListItemsOutput; import biz.paluch.clean.architecture.usecases.advanced.ListItemsImpl; /** * @author Mark Paluch * @since 02.08.13 12:45 */ @Stateless public class ItemService { @Inject private ItemRepository itemRepository; @Inject private ListItemsImpl listItems; @Inject private CreateOrUpdateItem createOrUpdateItem; /** * Wire all dependencies. */ @PostConstruct public void postConstruct() { createOrUpdateItem.setItemRepository(itemRepository); listItems.setItemRepository(itemRepository); } /** * List items and illustrate the use of an output boundary without an adapter. While this may look awkward, imagine this use * case might have a conversation with the output boundary. The use case does some action and talks to the output boundary * in order to push some intermediate results until the operation is finished. * * @return list of items. */ public List listItems() { final List results = new ArrayList<>(); listItems.listItems(new ListItemsOutput() { @Override public void onResponse(List items) { results.addAll(items); } }); return results; } /** * A simple use case without the need of an output boundary. * * @param item */ public void createOrUpdateItem(String item) { createOrUpdateItem.createOrUpdateItem(item); } public Item find(String itemId) { return itemRepository.find(itemId); } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/facade/OrderService.java ================================================ package biz.paluch.clean.architecture.facade; import java.util.List; import javax.ejb.Stateless; import javax.inject.Inject; import biz.paluch.clean.architecture.applicationmodel.NotFoundException; import biz.paluch.clean.architecture.applicationmodel.Order; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; import biz.paluch.clean.architecture.contracts.repositories.OrderRepository; import biz.paluch.clean.architecture.contracts.repositories.UserRepository; import biz.paluch.clean.architecture.contracts.usecases.PlaceOrderOutput; import biz.paluch.clean.architecture.contracts.usecases.PlaceOrderRequest; import biz.paluch.clean.architecture.di_example.PlaceOrderWithDependencies; import biz.paluch.clean.architecture.usecases.simple.CreateOrUpdateUser; import biz.paluch.clean.architecture.usecases.simple.ListOrders; /** * @author Mark Paluch * @since 02.08.13 12:45 */ @Stateless public class OrderService { @Inject private OrderRepository orderRepository; @Inject private UserRepository userRepository; @Inject private ItemRepository itemRepository; @Inject private CreateOrUpdateUser createOrUpdateUser; @Inject private ListOrders listOrders; @Inject private PlaceOrderWithDependencies placeOrder; public List listOrders() { return listOrders.listOrders(); } public String placeOrder(List items, String userName) throws NotFoundException { createOrUpdateUser.createOrUpdateUser(userName); PlaceOrderRequest request = new PlaceOrderRequest(); request.items = items; request.userName = userName; PlaceOrderResponse response = new PlaceOrderResponse(); placeOrder.placeOrder(request, response); return response.orderId; } private static class PlaceOrderResponse implements PlaceOrderOutput { public String orderId; @Override public void onResponse(String orderId) { this.orderId = orderId; } } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/frontend/jsf/ItemController.java ================================================ package biz.paluch.clean.architecture.frontend.jsf; import java.util.ArrayList; import java.util.List; import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.faces.model.SelectItem; import javax.inject.Inject; import javax.inject.Named; import biz.paluch.clean.architecture.applicationmodel.Item; import biz.paluch.clean.architecture.facade.ItemService; /** * @author Mark Paluch * @since 02.08.13 13:10 */ @RequestScoped @Named("itemController") public class ItemController { @EJB private ItemService itemService; @Inject private ItemModel itemModel; public List getItems() { List items = itemService.listItems(); List result = new ArrayList<>(); for (Item item : items) { SelectItem selectItem = new SelectItem(item.getItem(), item.getItem()); result.add(selectItem); } return result; } public void createOrUpdateItem() { itemService.createOrUpdateItem(itemModel.getItem()); itemModel.setItem(""); } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/frontend/jsf/ItemModel.java ================================================ package biz.paluch.clean.architecture.frontend.jsf; import java.io.Serializable; import javax.enterprise.inject.Model; /** * @author Mark Paluch * @since 02.08.13 13:10 */ @Model public class ItemModel implements Serializable { private String item; public String getItem() { return item; } public void setItem(String item) { this.item = item; } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/frontend/jsf/OrderController.java ================================================ package biz.paluch.clean.architecture.frontend.jsf; import java.util.ArrayList; import java.util.List; import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.inject.Inject; import javax.inject.Named; import biz.paluch.clean.architecture.facade.OrderService; /** * @author Mark Paluch * @since 02.08.13 13:41 */ @RequestScoped @Named("orderController") public class OrderController { @EJB private OrderService orderService; @Inject private OrderModel orderModel; public void addItem() { OrderItem oim = new OrderItem(); if (orderModel.getSelectedItem() != null && !"".equals(orderModel.getSelectedItem().trim())) { oim.setItem(orderModel.getSelectedItem()); orderModel.getOrderItems().add(oim); } } public void removeItem(OrderItem item) { orderModel.getOrderItems().remove(item); } public void createOrder() { List items = new ArrayList<>(); for (OrderItem orderItem : orderModel.getOrderItems()) { items.add(orderItem.getItem()); } String orderId = orderService.placeOrder(items, orderModel.getUserName()); orderModel.setUserName(null); orderModel.setSelectedItem(null); orderModel.setOrderItems(new ArrayList()); FacesContext.getCurrentInstance().addMessage("success", new FacesMessage("Created sucessfully an order with id " + orderId)); } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/frontend/jsf/OrderItem.java ================================================ package biz.paluch.clean.architecture.frontend.jsf; import java.io.Serializable; /** * @author Mark Paluch * @since 02.08.13 13:40 */ public class OrderItem implements Serializable { private String item; public String getItem() { return item; } public void setItem(String item) { this.item = item; } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/frontend/jsf/OrderModel.java ================================================ package biz.paluch.clean.architecture.frontend.jsf; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.enterprise.context.SessionScoped; import javax.enterprise.inject.Model; /** * @author Mark Paluch * @since 02.08.13 13:39 */ @Model @SessionScoped public class OrderModel implements Serializable { private String userName; private List orderItems = new ArrayList<>(); private String selectedItem; public String getSelectedItem() { return selectedItem; } public void setSelectedItem(String selectedItem) { this.selectedItem = selectedItem; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public List getOrderItems() { return orderItems; } public void setOrderItems(List orderItems) { this.orderItems = orderItems; } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/frontend/rest/ItemResource.java ================================================ package biz.paluch.clean.architecture.frontend.rest; import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; import biz.paluch.clean.architecture.applicationmodel.Item; import biz.paluch.clean.architecture.facade.ItemService; @Path("/items") @RequestScoped public class ItemResource { @EJB private ItemService itemService; @GET @Produces(MediaType.APPLICATION_XML) public ItemsRepresentation list() { ItemsRepresentation result = new ItemsRepresentation(); result.setItems(itemService.listItems()); return result; } @GET @Produces(MediaType.APPLICATION_XML) @Path("{itemId}") public JAXBElement get(@PathParam("itemId") String itemId) { Item item = itemService.find(itemId); if (item == null) { throw new WebApplicationException(Response.Status.NOT_FOUND); } return new JAXBElement<>(new QName("item"), Item.class, item); } @PUT @Consumes(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML) public void createOrUpdate(JAXBElement item) { itemService.createOrUpdateItem(item.getValue().getItem()); } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/frontend/rest/ItemsRepresentation.java ================================================ package biz.paluch.clean.architecture.frontend.rest; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlRootElement; import biz.paluch.clean.architecture.applicationmodel.Item; /** * @author Mark Paluch * @since 02.08.13 13:53 */ @XmlRootElement public class ItemsRepresentation { private List items = new ArrayList<>(); public List getItems() { return items; } public void setItems(List items) { this.items = items; } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/frontend/rest/JaxRsActivator.java ================================================ /* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual * contributors by the @authors tag. See the copyright.txt in the * distribution for a full listing of individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package biz.paluch.clean.architecture.frontend.rest; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; /** * A class extending {@link Application} and annotated with @ApplicationPath is the Java EE 6 "no XML" approach to activating * JAX-RS. * *

* Resources are served relative to the servlet path specified in the {@link ApplicationPath} annotation. *

*/ @ApplicationPath("/rest") public class JaxRsActivator extends Application { /* class body intentionally left blank */ } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/frontend/rest/OrderResource.java ================================================ package biz.paluch.clean.architecture.frontend.rest; import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import biz.paluch.clean.architecture.facade.OrderService; @Path("/orders") @RequestScoped public class OrderResource { @EJB private OrderService orderService; @GET @Produces(MediaType.APPLICATION_XML) public OrdersRepresentation list() { OrdersRepresentation result = new OrdersRepresentation(); result.setOrders(orderService.listOrders()); return result; } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/frontend/rest/OrdersRepresentation.java ================================================ package biz.paluch.clean.architecture.frontend.rest; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlRootElement; import biz.paluch.clean.architecture.applicationmodel.Order; /** * @author Mark Paluch * @since 02.08.13 13:56 */ @XmlRootElement public class OrdersRepresentation { private List orders = new ArrayList<>(); public List getOrders() { return orders; } public void setOrders(List orders) { this.orders = orders; } } ================================================ FILE: delivery/web/src/main/java/biz/paluch/clean/architecture/util/Resources.java ================================================ /* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual * contributors by the @authors tag. See the copyright.txt in the * distribution for a full listing of individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package biz.paluch.clean.architecture.util; import java.util.logging.Logger; import javax.enterprise.context.RequestScoped; import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.InjectionPoint; import javax.faces.context.FacesContext; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; /** * This class uses CDI to alias Java EE resources, such as the persistence context, to CDI beans * *

* Example injection on a managed bean field: *

* *
 * @Inject
 * private EntityManager em;
 * 
*/ public class Resources { // use @SuppressWarnings to tell IDE to ignore warnings about field not being referenced directly @SuppressWarnings("unused") @Produces @PersistenceContext private EntityManager em; @Produces public Logger produceLog(InjectionPoint injectionPoint) { return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); } @Produces @RequestScoped public FacesContext produceFacesContext() { return FacesContext.getCurrentInstance(); } } ================================================ FILE: delivery/web/src/main/resources/META-INF/persistence.xml ================================================ java:jboss/datasources/clean-architectureDS biz.paluch.clean.architecture.external.jpa.entity.ItemEntity biz.paluch.clean.architecture.external.jpa.entity.OrderEntity biz.paluch.clean.architecture.external.jpa.entity.OrderItemEntity biz.paluch.clean.architecture.external.jpa.entity.UserEntity false NONE AUTO ================================================ FILE: delivery/web/src/main/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: delivery/web/src/main/webapp/WEB-INF/clean-architecture-ds.xml ================================================ jdbc:h2:mem:clean-architecture;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1 h2 sa sa ================================================ FILE: delivery/web/src/main/webapp/WEB-INF/faces-config.xml ================================================ ================================================ FILE: delivery/web/src/main/webapp/WEB-INF/web.xml ================================================ Servlet 3.0 Web Application Faces Servlet javax.faces.webapp.FacesServlet 1 Faces Servlet *.xhtml index.xhtml ================================================ FILE: delivery/web/src/main/webapp/index.xhtml ================================================ Orders

Clean Architecture Demo

You can manage items and create an order with this frontend.

Guide

  1. Go to Manage Items and create a few items
  2. Come back to the Index and add some items into the order
  3. Enter a name into the name field
  4. Submit the order using "Create Order" and notice that an order record has been written to java:jboss/datasources/clean-architectureDS

Order

Your Name:
Items in Order: Item Remove Remove item

Add new Item


Manage Items
================================================ FILE: delivery/web/src/main/webapp/items.xhtml ================================================ Items

Items

Item

Create new Item

Back to Index
================================================ FILE: delivery/web/src/main/webapp/resources/css/screen.css ================================================ /* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual * contributors by the @authors tag. See the copyright.txt in the * distribution for a full listing of individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Core styles for the page */ body { margin: 0; padding: 0; background-color: #F1F1F1; font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; font-size: 0.8em; color:#363636; } #container { margin: 0 auto; padding: 0 20px 10px 20px; border-top: 5px solid #000000; border-left: 5px solid #8c8f91; border-right: 5px solid #8c8f91; border-bottom: 25px solid #8c8f91; width: 865px; /* subtract 40px from banner width for padding */ background: #FFFFFF; background-image: url(#{request.contextPath}/resources/gfx/headerbkg.png); background-repeat: repeat-x; padding-top: 30px; box-shadow: 3px 3px 15px #d5d5d5; } #content { float: left; width: 500px; margin: 20px; } #aside { font-size: 0.9em; width: 275px; float: left; margin: 20px 0px; border: 1px solid #D5D5D5; background: #F1F1F1; background-image: url(#{request.contextPath}/resources/gfx/asidebkg.png); background-repeat: repeat-x; padding: 20px; } #aside ul { padding-left: 30px; } .dualbrand { float: right; padding-right: 10px; } #footer { clear: both; text-align: center; color: #666666; font-size: 0.85em; } code { font-size: 1.1em; } a { color: #4a5d75; text-decoration: none; } a:hover { color: #369; text-decoration: underline; } h1 { color:#243446; font-size: 2.25em; } h2 { font-size: 1em; } h3 { color:#243446; } h4 { } h5 { } h6 { } /* Member registration styles */ span.invalid { padding-left: 3px; color: red; } form { padding: 1em; font: 80%/1 sans-serif; width: 375px; border: 1px solid #D5D5D5; } label { float: left; width: 15%; margin-left: 20px; margin-right: 0.5em; padding-top: 0.2em; text-align: right; font-weight: bold; color:#363636; } input { margin-bottom: 8px; } .register { float: left; margin-left: 85px; } /* ----- table style ------- */ /* = Simple Table style (black header, grey/white stripes */ .simpletablestyle { background-color:#E6E7E8; clear:both; width: 550px; } .simpletablestyle img { border:0px; } .simpletablestyle td { height:2em; padding-left: 6px; font-size:11px; padding:5px 5px; } .simpletablestyle th { background: url(#{request.contextPath}/resources/gfx/bkg-blkheader.png) black repeat-x top left; font-size:12px; font-weight:normal; padding:0 10px 0 5px; border-bottom:#999999 dotted 1px; } .simpletablestyle thead { background: url(#{request.contextPath}/resources/gfx/bkg-blkheader.png) black repeat-x top left; height:31px; font-size:10px; font-weight:bold; color:#FFFFFF; text-align:left; } .simpletablestyle .header a { color:#94aebd; } .simpletablestype tfoot { height: 20px; font-size: 10px; font-weight: bold; background-color: #EAECEE; text-align: center; } .simpletablestyle tr.header td { padding: 0px 10px 0px 5px; } .simpletablestyle .subheader { background-color: #e6e7e8; font-size:10px; font-weight:bold; color:#000000; text-align:left; } /* Using new CSS3 selectors for styling*/ .simpletablestyle tr:nth-child(odd) { background: #f4f3f3; } .simpletablestyle tr:nth-child(even) { background: #ffffff; } .simpletablestyle td a:hover { color:#3883ce; text-decoration:none; } ================================================ FILE: external/jpa-repository/pom.xml ================================================ 4.0.0 biz.paluch.clean.architecture clean-architecture 1.0-SNAPSHOT ../.. jpa-repository biz.paluch.clean.architecture contracts biz.paluch.clean.architecture application-model biz.paluch.clean.architecture commons javax.enterprise cdi-api provided org.jboss.spec.javax.annotation jboss-annotations-api_1.1_spec provided org.hibernate.javax.persistence hibernate-jpa-2.0-api provided org.jboss.spec.javax.ejb jboss-ejb-api_3.1_spec provided org.hibernate hibernate-jpamodelgen provided org.hibernate hibernate-core provided org.hibernate hibernate-entitymanager provided junit junit test com.h2database h2 test org.slf4j slf4j-api test org.slf4j slf4j-simple test org.mockito mockito-core test ================================================ FILE: external/jpa-repository/src/main/java/biz/paluch/clean/architecture/external/jpa/entity/ItemEntity.java ================================================ package biz.paluch.clean.architecture.external.jpa.entity; import javax.persistence.*; /** * @author Mark Paluch * @since 01.08.13 08:18 */ @Entity @Table(name = "Items") @NamedQueries({ @NamedQuery(name = ItemEntity.QUERY_FIND_BY_ITEM_NAME, query = "SELECT i from ItemEntity i where i.item = :item"), @NamedQuery(name = ItemEntity.QUERY_FIND_ALL, query = "SELECT i from ItemEntity i ") }) public class ItemEntity { public static final String QUERY_FIND_BY_ITEM_NAME = "ItemEntity.findByItem"; public static final String QUERY_FIND_ALL = "ItemEntity.findAll"; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Basic private String item; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getItem() { return item; } public void setItem(String item) { this.item = item; } } ================================================ FILE: external/jpa-repository/src/main/java/biz/paluch/clean/architecture/external/jpa/entity/OrderEntity.java ================================================ package biz.paluch.clean.architecture.external.jpa.entity; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.persistence.*; /** * @author Mark Paluch * @since 01.08.13 08:18 */ @Entity @Table(name = "Orders") @NamedQueries({ @NamedQuery(name = OrderEntity.QUERY_COUNT, query = "SELECT count(orderId) from OrderEntity"), @NamedQuery(name = OrderEntity.QUERY_FIND_BY_ORDERID, query = "SELECT o from OrderEntity o where o.orderId= :orderId"), @NamedQuery(name = OrderEntity.QUERY_FIND_ALL, query = "SELECT o from OrderEntity o ") }) public class OrderEntity { public static final String QUERY_COUNT = "OrderEntity.count"; public static final String QUERY_FIND_BY_ORDERID = "OrderEntity.findByOrderId"; public static final String QUERY_FIND_ALL = "OrderEntity.findAll"; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Basic private String orderId; @Basic private Date orderDate; @ManyToOne() private UserEntity createdBy; @OneToMany(cascade = CascadeType.ALL, mappedBy = "order") private List items = new ArrayList<>(); public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public Date getOrderDate() { return orderDate; } public void setOrderDate(Date orderDate) { this.orderDate = orderDate; } public UserEntity getCreatedBy() { return createdBy; } public void setCreatedBy(UserEntity createdBy) { this.createdBy = createdBy; } public List getItems() { return items; } public void setItems(List items) { this.items = items; } } ================================================ FILE: external/jpa-repository/src/main/java/biz/paluch/clean/architecture/external/jpa/entity/OrderItemEntity.java ================================================ package biz.paluch.clean.architecture.external.jpa.entity; import javax.persistence.*; /** * @author Mark Paluch * @since 01.08.13 08:18 */ @Entity @Table(name = "OrderItem") public class OrderItemEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "orderId") private OrderEntity order; @Basic private String orderItem; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public OrderEntity getOrder() { return order; } public void setOrder(OrderEntity order) { this.order = order; } public String getOrderItem() { return orderItem; } public void setOrderItem(String orderItem) { this.orderItem = orderItem; } } ================================================ FILE: external/jpa-repository/src/main/java/biz/paluch/clean/architecture/external/jpa/entity/UserEntity.java ================================================ package biz.paluch.clean.architecture.external.jpa.entity; import javax.persistence.*; /** * @author Mark Paluch * @since 01.08.13 07:24 */ @Entity @Table(name = "Users") @NamedQueries({ @NamedQuery(name = UserEntity.QUERY_FIND_BY_USERNAME, query = "SELECT u from UserEntity u where u.userName = :userName") }) public class UserEntity { public static final String QUERY_FIND_BY_USERNAME = "UserEntity.finyByUserName"; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Basic private String userName; public UserEntity() { } 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; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" [id=").append(id); sb.append(", userName='").append(userName).append('\''); sb.append(']'); return sb.toString(); } } ================================================ FILE: external/jpa-repository/src/main/java/biz/paluch/clean/architecture/external/jpa/repository/JpaItemRepository.java ================================================ package biz.paluch.clean.architecture.external.jpa.repository; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import javax.persistence.EntityManager; import biz.paluch.clean.architecture.applicationmodel.Item; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; import biz.paluch.clean.architecture.external.jpa.entity.ItemEntity; /** * @author Mark Paluch * @since 02.08.13 12:55 */ public class JpaItemRepository implements ItemRepository { @Inject private EntityManager entityManager; @Override public Item find(String item) { List list = entityManager.createNamedQuery(ItemEntity.QUERY_FIND_BY_ITEM_NAME, ItemEntity.class) .setParameter("item", item).getResultList(); if (list.isEmpty()) { return null; } ItemEntity itemEntity = list.get(0); return toItem(itemEntity); } private Item toItem(ItemEntity itemEntity) { Item result = new Item(); result.setItem(itemEntity.getItem()); return result; } @Override public void persist(Item item) { ItemEntity itemEntity = new ItemEntity(); itemEntity.setItem(item.getItem()); entityManager.persist(itemEntity); } @Override public List findAll() { List list = entityManager.createNamedQuery(ItemEntity.QUERY_FIND_ALL, ItemEntity.class).getResultList(); List result = new ArrayList<>(); for (ItemEntity itemEntity : list) { result.add(toItem(itemEntity)); } return result; } public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } } ================================================ FILE: external/jpa-repository/src/main/java/biz/paluch/clean/architecture/external/jpa/repository/JpaOrderRepository.java ================================================ package biz.paluch.clean.architecture.external.jpa.repository; import java.util.ArrayList; import java.util.List; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.persistence.EntityManager; import biz.paluch.clean.architecture.applicationmodel.Order; import biz.paluch.clean.architecture.applicationmodel.OrderItem; import biz.paluch.clean.architecture.applicationmodel.User; import biz.paluch.clean.architecture.contracts.repositories.OrderRepository; import biz.paluch.clean.architecture.external.jpa.entity.OrderEntity; import biz.paluch.clean.architecture.external.jpa.entity.OrderItemEntity; import biz.paluch.clean.architecture.external.jpa.entity.UserEntity; /** * @author Mark Paluch * @since 01.08.13 08:22 */ @ApplicationScoped public class JpaOrderRepository implements OrderRepository { @Inject private EntityManager entityManager; @Override public int getNextOrderId() { List maxOrderId = entityManager.createNamedQuery(OrderEntity.QUERY_COUNT).getResultList(); if (maxOrderId.isEmpty()) { return 1; } return maxOrderId.get(0).intValue() + 1; } @Override public void persist(Order order) { OrderEntity orderEntity = new OrderEntity(); orderEntity.setOrderDate(order.getOrderDate()); orderEntity.setOrderId(order.getOrderId()); UserEntity user = getUser(order.getCreatedBy().getUserName()); orderEntity.setCreatedBy(user); for (OrderItem orderItem : order.getItems()) { OrderItemEntity orderItemEntity = new OrderItemEntity(); orderItemEntity.setOrder(orderEntity); orderItemEntity.setOrderItem(orderItem.getOrderItem()); orderEntity.getItems().add(orderItemEntity); } entityManager.persist(orderEntity); } private UserEntity getUser(String userName) { return (UserEntity) entityManager.createNamedQuery(UserEntity.QUERY_FIND_BY_USERNAME) .setParameter("userName", userName).getSingleResult(); } public void deleteAll() { entityManager.createQuery("DELETE from " + OrderEntity.class.getSimpleName()).executeUpdate(); } public Order find(String orderId) { List list = entityManager.createNamedQuery(OrderEntity.QUERY_FIND_BY_ORDERID, OrderEntity.class) .setParameter("orderId", orderId).getResultList(); if (list.isEmpty()) { return null; } OrderEntity orderEntity = list.get(0); return toOrder(orderEntity); } private Order toOrder(OrderEntity orderEntity) { Order order = new Order(); order.setCreatedBy(new User(orderEntity.getCreatedBy().getUserName())); order.setOrderDate(orderEntity.getOrderDate()); order.setOrderId(orderEntity.getOrderId()); for (OrderItemEntity orderItemEntity : orderEntity.getItems()) { order.getItems().add(new OrderItem(orderItemEntity.getOrderItem())); } return order; } @Override public List findOrders() { List list = entityManager.createNamedQuery(OrderEntity.QUERY_FIND_ALL, OrderEntity.class).getResultList(); List result = new ArrayList<>(); for (OrderEntity orderEntity : list) { result.add(toOrder(orderEntity)); } return result; } public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } } ================================================ FILE: external/jpa-repository/src/main/java/biz/paluch/clean/architecture/external/jpa/repository/JpaUserRepository.java ================================================ package biz.paluch.clean.architecture.external.jpa.repository; import java.util.List; import javax.inject.Inject; import javax.persistence.EntityManager; import biz.paluch.clean.architecture.applicationmodel.User; import biz.paluch.clean.architecture.contracts.repositories.UserRepository; import biz.paluch.clean.architecture.external.jpa.entity.UserEntity; /** * @author Mark Paluch * @since 01.08.13 09:21 */ public class JpaUserRepository implements UserRepository { @Inject private EntityManager entityManager; @Override public User find(String userName) { List list = entityManager.createNamedQuery(UserEntity.QUERY_FIND_BY_USERNAME, UserEntity.class) .setParameter("userName", userName).getResultList(); if (!list.isEmpty()) { UserEntity userEntity = list.get(0); User user = new User(); user.setUserName(userEntity.getUserName()); return user; } return null; } @Override public void store(User user) { UserEntity userEntity = new UserEntity(); userEntity.setUserName(user.getUserName()); entityManager.persist(userEntity); } public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } } ================================================ FILE: external/jpa-repository/src/main/resources/META-INF/beans.xml ================================================ ================================================ FILE: external/jpa-repository/src/test/java/biz/paluch/clean/architecture/external/jpa/AbstractJpaTest.java ================================================ package biz.paluch.clean.architecture.external.jpa; import java.util.HashMap; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.h2.Driver; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.H2Dialect; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; /** * @author Mark Paluch * @since 13.06.13 21:28 */ public abstract class AbstractJpaTest { private static EntityManagerFactory entityManagerFactory; protected EntityManager entityManager; @BeforeClass public static void beforeClass() throws Exception { Map properties = getH2Properties(); entityManagerFactory = Persistence.createEntityManagerFactory("primary", properties); } @AfterClass public static void afterClass() throws Exception { entityManagerFactory.close(); } @Before public void before() throws Exception { entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); } @After public void after() throws Exception { entityManager.getTransaction().rollback(); entityManager.close(); } public static Map getH2Properties() { Map properties = new HashMap<>(); properties.put(AvailableSettings.DIALECT, H2Dialect.class.getName()); properties.put(AvailableSettings.URL, "jdbc:h2:mem:primary"); properties.put(AvailableSettings.USER, "SA"); properties.put(AvailableSettings.DRIVER, Driver.class.getName()); properties.put(AvailableSettings.HBM2DDL_AUTO, "create-drop"); return properties; } } ================================================ FILE: external/jpa-repository/src/test/java/biz/paluch/clean/architecture/external/jpa/JpaOrderRepositoryTest.java ================================================ package biz.paluch.clean.architecture.external.jpa; import static org.junit.Assert.*; import java.util.Date; import java.util.List; import org.junit.Test; import biz.paluch.clean.architecture.applicationmodel.Order; import biz.paluch.clean.architecture.applicationmodel.OrderItem; import biz.paluch.clean.architecture.applicationmodel.User; import biz.paluch.clean.architecture.external.jpa.repository.JpaOrderRepository; import biz.paluch.clean.architecture.external.jpa.repository.JpaUserRepository; /** * @author Mark Paluch * @since 01.08.13 08:50 */ public class JpaOrderRepositoryTest extends AbstractJpaTest { private JpaUserRepository jpaUserRepository = new JpaUserRepository(); private JpaOrderRepository jpaOrderRepository = new JpaOrderRepository(); @Override public void before() throws Exception { super.before(); jpaOrderRepository.setEntityManager(entityManager); jpaUserRepository.setEntityManager(entityManager); } @Test public void testGetNextOrderId() throws Exception { int result = jpaOrderRepository.getNextOrderId(); assertEquals(1, result); } @Test public void testPersistAndFind() throws Exception { jpaUserRepository.store(new User("mark")); entityManager.flush(); Order order = mockOrder(); jpaOrderRepository.persist(order); } private Order mockOrder() { Order order = new Order(); order.setCreatedBy(new User("mark")); order.setOrderDate(new Date()); order.setOrderId("mark-42"); order.getItems().add(new OrderItem("A")); order.getItems().add(new OrderItem("B")); order.getItems().add(new OrderItem("C")); return order; } @Test public void testFind() throws Exception { Order order = mockOrder(); createOrder(order); Order result = jpaOrderRepository.find(order.getOrderId()); assertEquals(order.getCreatedBy().getUserName(), result.getCreatedBy().getUserName()); assertEquals(order.getOrderId(), result.getOrderId()); assertEquals(order.getItems().size(), result.getItems().size()); } @Test public void testFindOrders() throws Exception { Order order = mockOrder(); createOrder(order); List result = jpaOrderRepository.findOrders(); assertEquals(1, result.size()); } private void createOrder(Order order) { jpaUserRepository.store(new User("mark")); jpaOrderRepository.persist(order); entityManager.flush(); } } ================================================ FILE: external/jpa-repository/src/test/resources/META-INF/persistence.xml ================================================ org.hibernate.ejb.HibernatePersistence biz.paluch.clean.architecture.external.jpa.entity.ItemEntity biz.paluch.clean.architecture.external.jpa.entity.OrderEntity biz.paluch.clean.architecture.external.jpa.entity.OrderItemEntity biz.paluch.clean.architecture.external.jpa.entity.UserEntity true NONE AUTO ================================================ FILE: pom.xml ================================================ 4.0.0 biz.paluch.clean.architecture clean-architecture 1.0-SNAPSHOT pom clean-architecture A Multi-Module example for a maven clean architecture project. http://www.paluch.biz/ Apache License, Version 2.0 repo http://www.apache.org/licenses/LICENSE-2.0.html application-model contracts commons use-cases external/jpa-repository delivery/web UTF-8 1.1.0.Alpha8 1.0.6.Final 2.3.1 2.10 2.6 1.8 1.8 biz.paluch.clean.architecture application-model ${project.version} biz.paluch.clean.architecture use-cases ${project.version} biz.paluch.clean.architecture jpa-repository ${project.version} biz.paluch.clean.architecture commons ${project.version} biz.paluch.clean.architecture contracts ${project.version} org.apache.commons commons-lang3 3.3.2 javax.inject javax.inject 1 provided org.jboss.bom jboss-javaee-6.0-with-tools ${version.jboss.bom} pom import org.jboss.bom jboss-javaee-6.0-with-hibernate ${version.jboss.bom} pom import com.h2database h2 1.4.191 test org.slf4j slf4j-api 1.7.2 test org.slf4j slf4j-simple 1.7.2 test org.mockito mockito-core 2.6.8 test org.jboss.resteasy resteasy-jaxrs 2.3.6.Final provided org.jboss.resteasy resteasy-jaxb-provider 2.3.6.Final provided clean package jboss-as:run maven-surefire-plugin 2.19.1 org.wildfly.plugins wildfly-maven-plugin ${version.wildfly.maven.plugin} true ================================================ FILE: use-cases/pom.xml ================================================ 4.0.0 biz.paluch.clean.architecture clean-architecture 1.0-SNAPSHOT use-cases biz.paluch.clean.architecture application-model biz.paluch.clean.architecture contracts biz.paluch.clean.architecture commons javax.inject javax.inject 1 provided org.mockito mockito-core test junit junit test ================================================ FILE: use-cases/src/main/java/biz/paluch/clean/architecture/usecases/advanced/CreateOrUpdateItemImpl.java ================================================ package biz.paluch.clean.architecture.usecases.advanced; import biz.paluch.clean.architecture.applicationmodel.Item; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; import biz.paluch.clean.architecture.contracts.usecases.CreateOrUpdateItem; /** * * This Use-Case class implements the input boundary {@link CreateOrUpdateItem} and requires a dependency to * {@link ItemRepository}. Everything within this class is POJO-style. There is no response boundary since the completion of the * use case does not create any result data structure. * * @author Mark Paluch * @since 02.08.13 13:02 */ public class CreateOrUpdateItemImpl implements CreateOrUpdateItem { private ItemRepository itemRepository; public void createOrUpdateItem(String item) { Item theItem = itemRepository.find(item); if (theItem == null) { theItem = new Item(); theItem.setItem(item); itemRepository.persist(theItem); } } public void setItemRepository(ItemRepository itemRepository) { this.itemRepository = itemRepository; } } ================================================ FILE: use-cases/src/main/java/biz/paluch/clean/architecture/usecases/advanced/ListItemsImpl.java ================================================ package biz.paluch.clean.architecture.usecases.advanced; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; import biz.paluch.clean.architecture.contracts.usecases.ListItems; import biz.paluch.clean.architecture.contracts.usecases.ListItemsOutput; /** * * This Use-Case class implements the input boundary {@link ListItems} and requires a dependency to {@link ItemRepository}. * Everything within this class is POJO-style. There is no response boundary since the completion of the use case does not * create any result data structure. * * @author Mark Paluch * @since 02.08.13 13:02 */ public class ListItemsImpl implements ListItems { private ItemRepository itemRepository; @Override public void listItems(ListItemsOutput listItemsOutput) { listItemsOutput.onResponse(itemRepository.findAll()); } @Override public void setItemRepository(ItemRepository itemRepository) { this.itemRepository = itemRepository; } } ================================================ FILE: use-cases/src/main/java/biz/paluch/clean/architecture/usecases/advanced/PlaceOrderImpl.java ================================================ package biz.paluch.clean.architecture.usecases.advanced; import java.util.List; import biz.paluch.clean.architecture.applicationmodel.NotFoundException; import biz.paluch.clean.architecture.applicationmodel.Order; import biz.paluch.clean.architecture.applicationmodel.OrderItem; import biz.paluch.clean.architecture.commons.DateProvider; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; import biz.paluch.clean.architecture.contracts.repositories.OrderRepository; import biz.paluch.clean.architecture.contracts.repositories.UserRepository; import biz.paluch.clean.architecture.contracts.usecases.PlaceOrder; import biz.paluch.clean.architecture.contracts.usecases.PlaceOrderOutput; import biz.paluch.clean.architecture.contracts.usecases.PlaceOrderRequest; import biz.paluch.clean.architecture.usecases.simple.ValidateOrder; /** * @author Mark Paluch * @since 01.08.13 07:15 */ public class PlaceOrderImpl implements PlaceOrder { private OrderRepository orderRepository; private ItemRepository itemRepository; private UserRepository userRepository; /** * Place order and return the OrderId. * * @param request * @param output */ @Override public void placeOrder(PlaceOrderRequest request, PlaceOrderOutput output) throws NotFoundException { ValidateOrder.newInstance(itemRepository, userRepository).validate(request.items, request.userName); String orderId = createOrderId(request.userName); Order order = constructOrder(orderId, request.items, request.userName); storeOrder(order); output.onResponse(order.getOrderId()); } private String createOrderId(String userName) { int nextOrderId = orderRepository.getNextOrderId(); return userName + "-" + nextOrderId; } private Order constructOrder(String orderId, List items, String userName) { Order order = new Order(); order.setOrderDate(DateProvider.get()); order.setOrderId(orderId); order.setCreatedBy(userRepository.find(userName)); for (String item : items) { OrderItem orderItem = new OrderItem(); orderItem.setOrderItem(item); order.getItems().add(orderItem); } return order; } private void storeOrder(Order order) { orderRepository.persist(order); } public void setOrderRepository(OrderRepository orderRepository) { this.orderRepository = orderRepository; } public void setItemRepository(ItemRepository itemRepository) { this.itemRepository = itemRepository; } public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } } ================================================ FILE: use-cases/src/main/java/biz/paluch/clean/architecture/usecases/simple/CreateOrUpdateUser.java ================================================ package biz.paluch.clean.architecture.usecases.simple; import javax.inject.Inject; import biz.paluch.clean.architecture.applicationmodel.User; import biz.paluch.clean.architecture.contracts.repositories.UserRepository; /** * @author Mark Paluch * @since 02.08.13 07:34 */ public class CreateOrUpdateUser { private UserRepository userRepository; public void createOrUpdateUser(String userName) { User user = userRepository.find(userName); if (user == null) { user = new User(userName); userRepository.store(user); } } @Inject public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } } ================================================ FILE: use-cases/src/main/java/biz/paluch/clean/architecture/usecases/simple/ListOrders.java ================================================ package biz.paluch.clean.architecture.usecases.simple; import java.util.List; import javax.inject.Inject; import biz.paluch.clean.architecture.applicationmodel.Order; import biz.paluch.clean.architecture.contracts.repositories.OrderRepository; /** * @author Mark Paluch * @since 01.08.13 07:22 */ public class ListOrders { private OrderRepository orderRepository; public List listOrders() { return orderRepository.findOrders(); } @Inject public void setOrderRepository(OrderRepository orderRepository) { this.orderRepository = orderRepository; } } ================================================ FILE: use-cases/src/main/java/biz/paluch/clean/architecture/usecases/simple/ValidateOrder.java ================================================ package biz.paluch.clean.architecture.usecases.simple; import java.util.List; import biz.paluch.clean.architecture.applicationmodel.Item; import biz.paluch.clean.architecture.applicationmodel.NotFoundException; import biz.paluch.clean.architecture.applicationmodel.User; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; import biz.paluch.clean.architecture.contracts.repositories.UserRepository; /** * @author Mark Paluch * @since 01.08.13 07:49 */ public class ValidateOrder { private ItemRepository itemRepository; private UserRepository userRepository; private ValidateOrder(ItemRepository itemRepository, UserRepository userRepository) { this.itemRepository = itemRepository; this.userRepository = userRepository; } public static ValidateOrder newInstance(ItemRepository itemRepository, UserRepository userRepository) { return new ValidateOrder(itemRepository, userRepository); } public void validate(List items, String userName) { validateItems(items); validateUser(userName); } private void validateUser(String userName) { User user = userRepository.find(userName); if (user == null) { throw new NotFoundException("User " + userName + " not found"); } } private void validateItems(List items) { for (String item : items) { validateItem(item); } } private void validateItem(String item) { Item itemInCatalog = itemRepository.find(item); if (itemInCatalog == null) { throw new NotFoundException("Item " + item + " not found"); } } } ================================================ FILE: use-cases/src/main/resources/META-INF/beans.xml ================================================ ================================================ FILE: use-cases/src/test/java/biz/paluch/clean/architecture/usecases/CreateOrUpdateUserTest.java ================================================ package biz.paluch.clean.architecture.usecases; import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; import biz.paluch.clean.architecture.usecases.simple.CreateOrUpdateUser; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import biz.paluch.clean.architecture.applicationmodel.User; import biz.paluch.clean.architecture.contracts.repositories.UserRepository; /** * @author Mark Paluch * @since 02.08.13 07:36 */ @RunWith(MockitoJUnitRunner.class) public class CreateOrUpdateUserTest { public static final String USER_NAME = "mark"; private CreateOrUpdateUser sut = new CreateOrUpdateUser(); @Mock private UserRepository userRepository; @Before public void before() throws Exception { sut.setUserRepository(userRepository); } @Test public void testCreateOrUpdateUser() throws Exception { sut.createOrUpdateUser(USER_NAME); verify(userRepository).store(any(User.class)); } } ================================================ FILE: use-cases/src/test/java/biz/paluch/clean/architecture/usecases/ListOrdersTest.java ================================================ package biz.paluch.clean.architecture.usecases; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.List; import biz.paluch.clean.architecture.usecases.simple.ListOrders; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import biz.paluch.clean.architecture.applicationmodel.Order; import biz.paluch.clean.architecture.contracts.repositories.OrderRepository; /** * @author Mark Paluch * @since 02.08.13 11:13 */ @RunWith(MockitoJUnitRunner.class) public class ListOrdersTest { private ListOrders sut = new ListOrders(); @Mock private OrderRepository orderRepository; @Before public void before() throws Exception { sut.setOrderRepository(orderRepository); } @Test public void testListOrders() throws Exception { when(orderRepository.findOrders()).thenReturn(Arrays.asList(new Order())); List result = sut.listOrders(); assertEquals(1, result.size()); } } ================================================ FILE: use-cases/src/test/java/biz/paluch/clean/architecture/usecases/PlaceOrderImplTest.java ================================================ package biz.paluch.clean.architecture.usecases; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Date; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import biz.paluch.clean.architecture.applicationmodel.Item; import biz.paluch.clean.architecture.applicationmodel.NotFoundException; import biz.paluch.clean.architecture.applicationmodel.Order; import biz.paluch.clean.architecture.applicationmodel.User; import biz.paluch.clean.architecture.commons.StaticDateProvider; import biz.paluch.clean.architecture.contracts.repositories.ItemRepository; import biz.paluch.clean.architecture.contracts.repositories.OrderRepository; import biz.paluch.clean.architecture.contracts.repositories.UserRepository; import biz.paluch.clean.architecture.contracts.usecases.PlaceOrderOutput; import biz.paluch.clean.architecture.contracts.usecases.PlaceOrderRequest; import biz.paluch.clean.architecture.usecases.advanced.PlaceOrderImpl; /** * @author Mark Paluch * @since 01.08.13 07:31 */ @RunWith(MockitoJUnitRunner.class) public class PlaceOrderImplTest { public static final String ITEM_NAME_SCISSORS = "Scissors"; public static final String ITEM_NAME_PAPER = "Paper"; public static final String ITEM_NAME_GLUE = "Glue"; public static final String USER_NAME_MARK = "mark"; private PlaceOrderImpl sut = new PlaceOrderImpl(); @Mock private OrderRepository orderRepository; @Mock private ItemRepository itemRepository; @Mock private UserRepository userRepository; @Mock private PlaceOrderOutput orderOutput; private PlaceOrderRequest request = new PlaceOrderRequest(); @Before public void setUp() throws Exception { sut.setItemRepository(itemRepository); sut.setOrderRepository(orderRepository); sut.setUserRepository(userRepository); } @Test public void testPlaceOrder() throws Exception { List items = Arrays.asList(ITEM_NAME_SCISSORS, ITEM_NAME_PAPER, ITEM_NAME_GLUE); mockItemRepository(); mockUserRepository(); mockOrderRepository(); request.items = items; request.userName = USER_NAME_MARK; sut.placeOrder(request, orderOutput); verify(orderOutput).onResponse("mark-42"); } @Test public void testPlaceOrderAndVerify() throws Exception { List items = Arrays.asList(ITEM_NAME_SCISSORS, ITEM_NAME_PAPER, ITEM_NAME_GLUE); mockItemRepository(); mockUserRepository(); mockOrderRepository(); Date orderDate = new Date(42424242L); StaticDateProvider.initialize(orderDate); request.items = items; request.userName = USER_NAME_MARK; sut.placeOrder(request, orderOutput); ArgumentCaptor captor = ArgumentCaptor.forClass(Order.class); verify(orderRepository).persist(captor.capture()); Order theOrder = captor.getValue(); assertEquals(orderDate, theOrder.getOrderDate()); assertEquals(USER_NAME_MARK, theOrder.getCreatedBy().getUserName()); assertEquals(items.size(), theOrder.getItems().size()); verify(orderOutput).onResponse("mark-42"); } private void mockOrderRepository() { when(orderRepository.getNextOrderId()).thenReturn(42); } private void mockUserRepository() { when(userRepository.find(USER_NAME_MARK)).thenReturn(new User(USER_NAME_MARK)); } private void mockItemRepository() { when(itemRepository.find(ITEM_NAME_SCISSORS)).thenReturn(new Item()); when(itemRepository.find(ITEM_NAME_PAPER)).thenReturn(new Item()); when(itemRepository.find(ITEM_NAME_GLUE)).thenReturn(new Item()); } @Test(expected = NotFoundException.class) public void testPlaceOrderItemNotFound() throws Exception { List items = Arrays.asList(ITEM_NAME_SCISSORS, ITEM_NAME_PAPER, ITEM_NAME_GLUE); request.items = items; request.userName = USER_NAME_MARK; sut.placeOrder(request, orderOutput); } @Test(expected = NotFoundException.class) public void testPlaceOrderUserNotFound() throws Exception { List items = Arrays.asList(ITEM_NAME_SCISSORS, ITEM_NAME_PAPER, ITEM_NAME_GLUE); mockItemRepository(); request.items = items; request.userName = USER_NAME_MARK; sut.placeOrder(request, orderOutput); } }