Skip to content

Commit f7e717d

Browse files
feat: Clean Architecture (#3235)
* #3230 - Clean Architecture. * #3230 - Clean Architecture. * #3230 - Clean Architecture. * #3230 - Clean Architecture. * #3230 - Clean Architecture Sonar. * #3230 - Clean Architecture Sonar. * #3230 - Clean Architecture Sonar. * #3230 - Clean Architecture Sonar. --------- Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
1 parent d823283 commit f7e717d

20 files changed

Lines changed: 1303 additions & 3 deletions

clean-architecture/README.md

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
---
2+
title: "Clean Architecture - A Software Maintainable Architectural style."
3+
shortTitle: Clean Architecture
4+
description: "Learn the Clean Architecture Style in Java with real-world examples, code snippets, and class diagrams. Enhance your coding skills with our detailed explanations."
5+
category: Behavioral
6+
language: en
7+
tag:
8+
- Decoupling
9+
- Architectural Style
10+
---
11+
12+
## Also known as
13+
14+
* Hexagonal Architecture.
15+
16+
## Intent of Clean Architecture.
17+
18+
The clean architecture is a software design architectural style which ensures the software application is easy to understand, maintainable and can be extend easily as per business requirement.
19+
20+
## Detailed Explanation of Clean Architecture Pattern with Real-World Examples
21+
22+
Real World.
23+
24+
A real world example of clean architecture is like teh shopping mall example. There some employee is assigned to work on the filling of the products in the counter, one person is responsible for the billing purpose, one person is taking care of the security, one person is taking care of the product they have in storage. The work of every individual is separate and they are focussed on the specific task. Clean architecture also suggests to make the component separate and each component should perform some task. Clean Architecture proposes a layered architecture with clear boundaries between different system components to achieve independence of frameworks, UI, databases, and delivery mechanisms and the possibility to test in isolation.
25+
26+
In plain word
27+
28+
It helps to make the system more maintainable and easy to extend.
29+
30+
Wikipedia says
31+
32+
> The clean architecture proposed by Robert C. Martin in 2012 combines the principles of the hexagonal architecture, the onion architecture and several other variants. It provides additional levels of detail of the component, which are presented as concentric rings. It isolates adapters and interfaces (user interface, databases, external systems, devices) in the outer rings of the architecture and leaves the inner rings for use cases and entities.
33+
>
34+
> The clean architecture uses the principle of dependency inversion with the strict rule that dependencies shall only exist between an outer ring to an inner ring and never the contrary.
35+
36+
37+
## Clean architecture Class Diagram
38+
39+
![Clean Architecture](./etc/cleanArchitectureUMLDiagram.png "Clean Architecture class diagram")
40+
41+
## When to Use the Clean Architecture Pattern in Java
42+
43+
In all application we can use the clean architecture style and make the component separate and business logic separate from the UI and database.
44+
45+
## Real-World Applications of Chain of Responsibility Pattern in Java.
46+
47+
In the application say Ecommerce application user gives teh order and the application is represented using teh clean architecture pattern.
48+
49+
There are facility like the **product** where user can see the product details like the price and the features, **Cart** user can add the product they have selected and the **Order** where user can see the total order and calculate the price of the order. Learn how to implement this design pattern in Java with the following code snippet.
50+
51+
## Programmatic Example of Clean Architecture Pattern
52+
53+
First we have the entity class like the `Product`, `Order` and teh `Cart`
54+
```java
55+
public class Product {
56+
private String id;
57+
private String name;
58+
private double price;
59+
60+
public Product(String id, String name, double price) {
61+
this.id = id;
62+
this.name = name;
63+
this.price = price;
64+
}
65+
}
66+
```
67+
68+
```java
69+
public class Cart {
70+
private Product product;
71+
private int quantity;
72+
73+
public CartItem(Product product, int quantity) {
74+
this.product = product;
75+
this.quantity = quantity;
76+
}
77+
78+
public double getTotalPrice() {
79+
return product.getPrice() * quantity;
80+
}
81+
}
82+
```
83+
84+
```java
85+
public class Order {
86+
private String orderId;
87+
private List<CartItem> items;
88+
private double totalPrice;
89+
90+
public Order(String orderId, List<CartItem> items) {
91+
this.orderId = orderId;
92+
this.items = items;
93+
this.totalPrice = items.stream().mapToDouble(CartItem::getTotalPrice).sum();
94+
}
95+
}
96+
```
97+
The repository interfaces are created.
98+
```java
99+
public interface CartRepository {
100+
void addItemToCart(String userId, Product product, int quantity);
101+
void removeItemFromCart(String userId, String productId);
102+
List<Cart> getItemsInCart(String userId);
103+
double calculateTotal(String userId);
104+
void clearCart(String userId);
105+
}
106+
```
107+
```java
108+
public interface ProductRepository {
109+
Product getProductById(String productId);
110+
}
111+
```
112+
```java
113+
public interface OrderRepository {
114+
void saveOrder(Order order);
115+
}
116+
```
117+
118+
119+
The in memory data store in the cart and order.
120+
```java
121+
public class InMemoryCartRepository implements CartRepository {
122+
private final Map<String, List<Cart>> userCarts = new HashMap<>();
123+
124+
@Override
125+
public void addItemToCart(String userId, Product product, int quantity) {
126+
List<Cart> cart = userCarts.getOrDefault(userId, new ArrayList<>());
127+
cart.add(new Cart(product, quantity));
128+
userCarts.put(userId, cart);
129+
}
130+
131+
@Override
132+
public void removeItemFromCart(String userId, String productId) {
133+
List<Cart> cart = userCarts.get(userId);
134+
if (cart != null) {
135+
cart.removeIf(item -> item.getProduct().getId().equals(productId));
136+
}
137+
}
138+
139+
@Override
140+
public List<Cart> getItemsInCart(String userId) {
141+
return userCarts.getOrDefault(userId, new ArrayList<>());
142+
}
143+
144+
@Override
145+
public double calculateTotal(String userId) {
146+
return userCarts.getOrDefault(userId, new ArrayList<>())
147+
.stream()
148+
.mapToDouble(Cart::getTotalPrice)
149+
.sum();
150+
}
151+
152+
@Override
153+
public void clearCart(String userId) {
154+
userCarts.remove(userId);
155+
}
156+
}
157+
```
158+
```java
159+
public class InMemoryOrderRepository implements OrderRepository {
160+
private final List<Order> orders = new ArrayList<>();
161+
162+
@Override
163+
public void saveOrder(Order order) {
164+
orders.add(order);
165+
}
166+
}
167+
```
168+
169+
```java
170+
public class InMemoryProductRepository implements ProductRepository {
171+
private final Map<String, Product> products = new HashMap<>();
172+
173+
public InMemoryProductRepository() {
174+
products.put("1", new Product("1", "Laptop", 1000.0));
175+
products.put("2", new Product("2", "Smartphone", 500.0));
176+
}
177+
178+
@Override
179+
public Product getProductById(String productId) {
180+
return products.get(productId);
181+
}
182+
}
183+
```
184+
185+
The order controller.
186+
```java
187+
public class OrderController{
188+
private final ShoppingCartService shoppingCartUseCase;
189+
190+
public OrderController(ShoppingCartService shoppingCartUseCase) {
191+
this.shoppingCartUseCase = shoppingCartUseCase;
192+
}
193+
194+
public Order checkout(String userId) {
195+
return shoppingCartUseCase.checkout(userId);
196+
}
197+
}
198+
```
199+
The cart controller.
200+
```java
201+
public class CartController {
202+
private final ShoppingCartService shoppingCartUseCase;
203+
204+
public CartController(ShoppingCartService shoppingCartUseCase) {
205+
this.shoppingCartUseCase = shoppingCartUseCase;
206+
}
207+
208+
public void addItemToCart(String userId, String productId, int quantity) {
209+
shoppingCartUseCase.addItemToCart(userId, productId, quantity);
210+
}
211+
212+
public void removeItemFromCart(String userId, String productId) {
213+
shoppingCartUseCase.removeItemFromCart(userId, productId);
214+
}
215+
216+
public double calculateTotal(String userId) {
217+
return shoppingCartUseCase.calculateTotal(userId);
218+
}
219+
}
220+
```
221+
222+
The clean architecture in action.
223+
```java
224+
public static void main(String[] args) {
225+
226+
ProductRepository productRepository = new InMemoryProductRepository();
227+
CartRepository cartRepository = new InMemoryCartRepository();
228+
OrderRepository orderRepository = new InMemoryOrderRepository();
229+
230+
ShoppingCartService shoppingCartUseCase =
231+
new ShoppingCartService(productRepository, cartRepository, orderRepository);
232+
233+
CartController cartController = new CartController(shoppingCartUseCase);
234+
OrderController orderController = new OrderController(shoppingCartUseCase);
235+
236+
String userId = "user123";
237+
cartController.addItemToCart(userId, "1", 1);
238+
cartController.addItemToCart(userId, "2", 2);
239+
240+
System.out.println("Total: $" + cartController.calculateTotal(userId));
241+
242+
Order order = orderController.checkout(userId);
243+
System.out.println(
244+
"Order placed! Order ID: " + order.getOrderId() + ", Total: $" + order.getTotalPrice());
245+
}
246+
```
247+
248+
The output of the code.
249+
```md
250+
Total: $2000.0
251+
Order placed! Order ID: ORDER-1743349969254, Total: $2000.0
252+
```
253+
254+
## Benefits and Trade-offs of Clean Architecture Pattern.
255+
256+
Benefits:
257+
258+
The main benefits of the Clean Architecture involves -
259+
**Scalability** - It allows to add new features without any issue.
260+
**Modularity** - It makes the code loosely coupled and making the change in any component becomes easier.
261+
**Testability** - The architecture promotes unit testing, integration testing, and acceptance testing of different layers independently.
262+
263+
Trade-Offs:
264+
265+
Initially the design needs to be done with high precision and the UML diagram should cover all the architectural structure. It will in return help to make it more channelised and the code extensibility will increase.
266+
267+
## Related Java Design Patterns
268+
269+
* Dependency Injection - Dependency Injection (DI) is a key concept in Clean Architecture. It promotes loose coupling between classes by allowing dependencies to be injected rather than directly created by the class itself.
270+
* Singleton Pattern - The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance. This is often used for shared services or resources, such as a configuration manager, logging service, or database connection pool.
271+
272+
## References and Credits
273+
274+
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
275+
* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
276+
* [Pattern-Oriented Software Architecture, Volume 1: A System of Patterns](https://amzn.to/3PAJUg5)
277+
* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
278+
* [Pattern languages of program design 3](https://amzn.to/4a4NxTH)
42.4 KB
Loading

clean-architecture/pom.xml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
5+
6+
The MIT License
7+
Copyright © 2014-2022 Ilkka Seppälä
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in
17+
all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
27+
-->
28+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
29+
<modelVersion>4.0.0</modelVersion>
30+
<parent>
31+
<groupId>com.iluwatar</groupId>
32+
<artifactId>java-design-patterns</artifactId>
33+
<version>1.26.0-SNAPSHOT</version>
34+
</parent>
35+
<artifactId>clean-architecture</artifactId>
36+
<dependencies>
37+
<dependency>
38+
<groupId>org.junit.jupiter</groupId>
39+
<artifactId>junit-jupiter-engine</artifactId>
40+
<scope>test</scope>
41+
</dependency>
42+
</dependencies>
43+
<build>
44+
<plugins>
45+
<plugin>
46+
<groupId>org.apache.maven.plugins</groupId>
47+
<artifactId>maven-assembly-plugin</artifactId>
48+
<executions>
49+
<execution>
50+
<configuration>
51+
<archive>
52+
<manifest>
53+
<mainClass>com.iluwatar.cleanarchitecture.App</mainClass>
54+
</manifest>
55+
</archive>
56+
</configuration>
57+
</execution>
58+
</executions>
59+
</plugin>
60+
</plugins>
61+
</build>
62+
</project>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.iluwatar.cleanarchitecture;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
5+
/**
6+
* Clean Architecture ensures separation of concerns by organizing code,
7+
* into layers and making it scalable and maintainable.
8+
*
9+
* <p>In the example there are Entities (Core Models) – Product, Cart,
10+
* Order handle business logic.
11+
* Use Cases (Application Logic) – ShoppingCartService manages
12+
* operations like adding items and checkout.
13+
* Interfaces & Adapters – Repositories (CartRepository, OrderRepository)
14+
* abstract data handling,
15+
* while controllers (CartController, OrderController) manage interactions.
16+
*/
17+
@Slf4j
18+
public final class App {
19+
20+
private App() {
21+
throw new UnsupportedOperationException("Utility class");
22+
}
23+
24+
/**
25+
* Program entry point.
26+
*
27+
* @param args command line args
28+
*/
29+
public static void main(final String[] args) {
30+
ProductRepository productRepository = new InMemoryProductRepository();
31+
CartRepository cartRepository = new InMemoryCartRepository();
32+
OrderRepository orderRepository = new InMemoryOrderRepository();
33+
34+
ShoppingCartService
35+
shoppingCartUseCase =
36+
new ShoppingCartService(
37+
productRepository, cartRepository, orderRepository);
38+
39+
CartController cartController = new CartController(shoppingCartUseCase);
40+
OrderController orderController = new OrderController(shoppingCartUseCase);
41+
42+
String userId = "user123";
43+
cartController.addItemToCart(userId, "1", 1);
44+
cartController.addItemToCart(userId, "2", 2);
45+
46+
Order order = orderController.checkout(userId);
47+
LOGGER.info("Total: ${}" + cartController.calculateTotal(userId));
48+
49+
LOGGER.info("Order placed! Order ID: {}, Total: ${}",
50+
order.getOrderId(), order.getTotalPrice());
51+
}
52+
}

0 commit comments

Comments
 (0)