Main Component
(Clean Architecture by Robert C. Martin)
The Main Component chapter discusses the core entry point of a software system and its role in initializing the application, wiring dependencies, and starting the execution flow. The main component serves as the highest-level module, orchestrating the application setup without containing any business logic.
Key Concepts of the Main Component
1. Purpose of the Main Component
- The main component is responsible for:
- Initializing the system by setting up all necessary components, services, and dependencies.
- Wiring dependencies between different layers and modules.
- Delegating execution to the appropriate use cases or controllers.
- It is not responsible for business logic or core application workflows. Its job is purely organizational and bootstrapping.
2. The Dependency Rule in the Main Component
- The main component must follow the dependency rule:
- It depends on the core layers (e.g., entities, use cases) but not the other way around.
- Example:
- The main component can initialize use cases, repositories, and frameworks, but these should not depend on the main component.
3. Structure of the Main Component
- The main component is often located in the outermost layer of the architecture.
- It contains the entry point of the application (e.g.,
main()
function or equivalent in your framework).
4. Responsibilities of the Main Component
a. Dependency Injection:
- The main component wires dependencies by providing concrete implementations of interfaces.
- Example:javaCopyEdit
public class Main {
public static void main(String[] args) {
OrderRepository orderRepository = new SqlOrderRepository();
PlaceOrderUseCase placeOrderUseCase = new PlaceOrderUseCase(orderRepository);
OrderController orderController = new OrderController(placeOrderUseCase);
}
}
b. Configuration:
- The main component configures external systems (e.g., database connections, API keys).
- It loads configuration settings, such as environment variables or application properties.
c. Application Startup:
- Starts necessary services (e.g., web servers, background jobs) and delegates control to the appropriate entry point.
Example in a Web Application:
public class Main {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
}
d. Integrating Frameworks:
- Frameworks like Spring, Django, or Express are often initialized in the main component.
- This keeps frameworks confined to the outermost layer, following the framework independence principle.
5. What the Main Component Should Avoid
- Avoid Business Logic:
- The main component should not implement use cases or business rules.
- Example of bad practice:
public static void main(String[] args) {
// Business logic directly in the main function
System.out.println("Processing order...");
}
Avoid Tight Coupling:
- The main component should not directly couple frameworks with core logic.
Example of a Main Component
E-Commerce System Example
Entities Layer:
public class Order {
private List<Product> products;
public double calculateTotal() {
return products.stream().mapToDouble(Product::getPrice).sum();
}
}
Use Case Layer:
public class PlaceOrderUseCase {
private OrderRepository orderRepository;
public PlaceOrderUseCase(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public void execute(Order order) {
orderRepository.save(order);
}
}
Interface Adapter Layer:
@RestController
public class OrderController {
private PlaceOrderUseCase placeOrderUseCase;
public OrderController(PlaceOrderUseCase placeOrderUseCase) {
this.placeOrderUseCase = placeOrderUseCase;
}
@PostMapping("/orders")
public ResponseEntity<String> placeOrder(@RequestBody Order order) {
placeOrderUseCase.execute(order);
return ResponseEntity.ok("Order placed successfully");
}
}
Main component
public class Main {
public static void main(String[] args) {
// Dependency wiring
OrderRepository orderRepository = new SqlOrderRepository();
PlaceOrderUseCase placeOrderUseCase = new PlaceOrderUseCase(orderRepository);
OrderController orderController = new OrderController(placeOrderUseCase);
// Framework initialization (e.g., starting the server)
SpringApplication.run(ApplicationConfig.class, args);
}
}
6. Testing the Main Component
- The main component itself typically doesn’t require detailed testing since it contains no business logic.
- Focus on testing the components it initializes (e.g., use cases, repositories).
Key Takeaways
- Purpose: The main component is responsible for initializing and wiring the system but contains no business logic.
- Dependency Rule: It depends on the core application but is independent of it.
- Framework Isolation: Frameworks are confined to the main component or outer layers.
- Modular Startup: Each component (e.g., use cases, repositories) is initialized separately, maintaining clear boundaries.