Chapter 21: Screaming Architecture & The Clean Architecture
Chapter 21: Screaming Architecture
Key Concepts
- Architectural Intent: The structure of the project should immediately reflect its core purpose (e.g., “hospital management system”), not the frameworks used (e.g., “Spring MVC app”).
- Framework Independence: Avoid letting frameworks dictate your architecture. Frameworks are tools, not the central theme.
- Testability: A screaming architecture enables easy testing by isolating business rules from technical details.
Code Example: Web-Based Order System
Scenario: Build an order management system where the architecture “screams” the domain (orders), not the web framework.
Project Structure
src/
orders/
domain/
Order.java // Core business entity
OrderService.java // Business logic
infrastructure/
WebController.java // Framework adapter
Code Snippets
- Order Entity (Domain Layer)
// Order.java
public class Order {
private String id;
private String product;
private int quantity;
public Order(String product, int quantity) {
this.id = UUID.randomUUID().toString();
this.product = product;
this.quantity = quantity;
}
// Getters and business methods
public String getId() { return id; }
public String getProduct() { return product; }
public int getQuantity() { return quantity; }
}
- Order Service (Business Logic)
// OrderService.java
public class OrderService {
public Order createOrder(String product, int quantity) {
return new Order(product, quantity);
}
}
- Web Controller (Framework Adapter)
// WebController.java
public class WebController {
private final OrderService orderService;
public WebController(OrderService orderService) {
this.orderService = orderService;
}
public String handleRequest(String product, int quantity) {
Order order = orderService.createOrder(product, quantity);
return "Order created: " + order.getId();
}
}
- Main Function (Test Case)
public class Main {
public static void main(String[] args) {
OrderService service = new OrderService();
WebController controller = new WebController(service);
// Simulate HTTP request
String response = controller.handleRequest("Laptop", 2);
System.out.println(response);
// Output: "Order created: d3b07384-d113-4ec6..."
}
}
Key Points:
- The
orders/domain
package focuses purely on business rules. - The
infrastructure
layer adapts the domain to the web framework. - The project structure emphasizes orders, not the web framework.
Chapter 22: The Clean Architecture
Key Concepts
- Dependency Rule: Source code dependencies point inward. Outer layers (UI, DB) depend on inner layers (business rules).
- Layers:
- Entities: Core business objects (e.g.,
Order
). - Use Cases: Application-specific business rules.
- Interface Adapters: Convert data between layers (e.g., database gateways).
- Frameworks/Drivers: External tools (e.g., Spring, Hibernate).
- Entities: Core business objects (e.g.,
- Independence: Layers can be developed/tested independently.
Code Example: Order Management System
Project Structure
src/
clean/
entities/
Order.java
usecases/
OrderInteractor.java
interfaces/
OrderPresenter.java
OrderGateway.java
main/
App.java // Composition root
Code Snippets
- Entity Layer (
Order
)
// Order.java (same as Chapter 21)
public class Order { ... }
- Use Case Layer (
OrderInteractor
)
// OrderInteractor.java
public class OrderInteractor {
private final OrderGateway gateway;
public OrderInteractor(OrderGateway gateway) {
this.gateway = gateway;
}
public void createOrder(String product, int quantity) {
Order order = new Order(product, quantity);
gateway.save(order);
}
}
- Interface Adapters
// OrderPresenter.java (Output boundary)
public interface OrderPresenter {
void displayOrder(String orderId);
}
// OrderGateway.java (Data access interface)
public interface OrderGateway {
void save(Order order);
}
- Database Adapter (Implements
OrderGateway
)
// DatabaseAdapter.java
public class DatabaseAdapter implements OrderGateway {
@Override
public void save(Order order) {
System.out.println("Saving order to DB: " + order.getId());
}
}
- Main Function (Dependency Injection)
// App.java
public class App {
public static void main(String[] args) {
OrderGateway gateway = new DatabaseAdapter();
OrderInteractor interactor = new OrderInteractor(gateway);
interactor.createOrder("Monitor", 1);
// Output: "Saving order to DB: 6f9b0b3e-..."
}
}
Key Points:
- Dependency Inversion:
OrderInteractor
depends on the abstractOrderGateway
, not the concreteDatabaseAdapter
. - Testability: Mock
OrderGateway
to test business logic without a real database. - Layered Compilation:
- Entities have no dependencies.
- Use cases depend only on entities.
- Adapters depend on use cases and frameworks.
Testing the Code
-
Compile & Run
Save files in the above structure and compile:javac clean/entities/*.java clean/usecases/*.java clean/interfaces/*.java clean/main/App.java java clean/main/App
-
Expected Output
Saving order to DB: 6f9b0b3e-...
Summary
- Screaming Architecture: Structure reflects domain, not frameworks.
- Clean Architecture Layers:
- Inner layers (entities, use cases) are framework-agnostic.
- Outer layers (adapters, UI) depend inward.
- Dependency Rule: High-level policies (business rules) never depend on low-level details (databases).
Multiple-Choice Questions
-
Which principles ensure the core business logic remains independent of frameworks?
A. Use the Humble Object Pattern for UI logic.
B. Define all database schemas in the Entities layer.
C. Let the web framework dictate package structure.
D. Invert dependencies using interfaces. -
Which layers in Clean Architecture are allowed to depend on external libraries?
A. Entities
B. Use Cases
C. Interface Adapters
D. Frameworks & Drivers -
How should data transfer between layers be handled?
A. Pass raw database entities directly to the UI.
B. Use DTOs to decouple layers.
C. Allow inner layers to reference outer-layer data structures.
D. Share domain objects across all layers. -
What violates the Dependency Rule in Clean Architecture?
A. A REST controller depending on a Use Case interface.
B. A Use Case depending on a database gateway interface.
C. An Entity importing a web framework class.
D. A database implementation depending on an Entity. -
Which practices make architecture “scream” the system’s purpose?
A. Naming packagescontrollers
andmodels
.
B. Organizing code around domain concepts likeLoan
orInvoice
.
C. Using framework-specific annotations in core logic.
D. Isolate I/O operations using the Humble Object Pattern. -
Which are true about the Humble Object Pattern?
A. It separates UI logic from business rules.
B. It allows direct database access in Entities.
C. It ensures testable core logic by isolating volatile code.
D. It requires frameworks to define business rules. -
What ensures inner layers remain stable?
A. Keeping inner layers free of framework dependencies.
B. Allowing Use Cases to depend on UI components.
C. Defining interfaces in outer layers for inner layers to implement.
D. Using dependency injection to invert control. -
Which scenarios violate the Interface Segregation Principle (ISP) in Clean Architecture?
A. A Use Case interface with methods for bothcreateOrder
andgenerateReport
.
B. Separate interfaces forOrderRepository
andReportRepository
.
C. A singleUser
entity containing both authentication and profile data.
D. A REST controller implementing a Use Case interface. -
How does the Dependency Inversion Principle (DIP) apply to Clean Architecture?
A. Inner layers define interfaces implemented by outer layers.
B. Use Cases depend on concrete database implementations.
C. Entities contain framework-specific annotations.
D. Outer layers define interfaces for inner layers to use. -
Which code snippets follow Clean Architecture principles?
A. A Use Case class importing a Spring@RestController
.
B. A database gateway implementing an interface defined in the Use Case layer.
C. An Entity class with JPA annotations.
D. A DTO passed from a controller to a Use Case.
Answers & Explanations
1. A, D
- A: The Humble Object Pattern isolates UI/database logic.
- D: Dependency inversion decouples core logic from implementations.
- B/C violate independence by tying core logic to frameworks.
2. C, D
- C/D: Only outer layers (Interface Adapters, Frameworks) depend on external tools.
- A/B (inner layers) must remain pure.
3. B
- B: DTOs prevent layer coupling.
- A/C/D create unwanted dependencies or expose internals.
4. C
- C: Entities (inner layer) must not reference outer layers (e.g., web frameworks).
- A/B/D follow the Dependency Rule.
5. B, D
- B: Domain-centric naming reflects purpose.
- D: Isolating I/O keeps core logic focused.
- A/C prioritize frameworks over domain.
6. A, C
- A/C: The pattern isolates testable logic from I/O.
- B/D mix concerns or invert dependencies.
7. A, D
- A: No framework ties in inner layers.
- D: Dependency injection inverts control.
- B/C violate the Dependency Rule.
8. A, C
- A: Fat interfaces force unnecessary dependencies.
- C: Mixing concerns in entities violates ISP.
- B/D adhere to ISP.
9. A
- A: Inner layers define abstractions; outer layers implement them.
- B/C/D invert or violate dependencies.
10. B, D
- B: Gateways implement inner-layer interfaces.
- D: DTOs decouple layers.
- A/C tie core logic to frameworks.
Code Example (Java)
// Inner Layers (Entities and Use Cases)
interface OrderRepository {
void save(Order order);
}
class Order {
private String id;
// No framework annotations or dependencies!
}
class PlaceOrderUseCase {
private final OrderRepository repository;
public PlaceOrderUseCase(OrderRepository repository) {
this.repository = repository;
}
public void execute(Order order) {
// Business logic here
repository.save(order);
}
}
// Outer Layer (Interface Adapter)
class DatabaseGateway implements OrderRepository {
@Override
public void save(Order order) {
// JPA/Hibernate implementation
}
}
// Framework Layer (Spring Controller)
@RestController
class OrderController {
private final PlaceOrderUseCase useCase;
public OrderController(PlaceOrderUseCase useCase) {
this.useCase = useCase;
}
@PostMapping("/order")
public void createOrder(@RequestBody OrderDto dto) {
Order order = convertToEntity(dto);
useCase.execute(order);
}
private Order convertToEntity(OrderDto dto) {
// Convert DTO to Entity
}
}
// DTO for data transfer
class OrderDto {
public String id;
}
// Main (Test Case)
public class Main {
public static void main(String[] args) {
OrderRepository repository = new DatabaseGateway();
PlaceOrderUseCase useCase = new PlaceOrderUseCase(repository);
OrderController controller = new OrderController(useCase);
OrderDto dto = new OrderDto();
dto.id = "123";
controller.createOrder(dto); // Output: Order saved via gateway
}
}
Key Compliance:
- Inner layers (
Order
,PlaceOrderUseCase
) have no framework dependencies. - Dependencies point inward (e.g.,
OrderController
depends onPlaceOrderUseCase
). - DTOs isolate data transfer.
- Interfaces (
OrderRepository
) invert dependencies.