Interface Adapters: Controllers and Presenters
Learning Objectives: By the end of this lesson, you will be able to:
- Explain the role of interface adapters in Clean Architecture
- Design and implement controllers and presenters in Java
- Understand how data is converted between layers and why this separation matters
1. Welcome and Introduction (5 minutes)
Icebreaker Activity
Think about a time when you had to change how data was displayed or received in an app. Did you have to change your core logic, or was it isolated? Why is it important to separate how data is presented from how it is processed?
Lesson Overview
- Duration: 90 minutes
- Prerequisites: Understanding of entities and use cases
- Materials Needed: Java development environment, pen and paper for sketching adapters
2. Learning Objectives and Session Outline (5 minutes)
What We’ll Cover Today:
- What are interface adapters?
- The roles of controllers and presenters
- How data is converted between layers
- Implementing controllers and presenters in Java
- Hands-on practice and exercises
- Common pitfalls and how to avoid them
Success Criteria:
By the end of this session, you should be able to design and implement controllers and presenters that convert data between the UI/framework and your core logic.
3. Foundation Knowledge (15-20 minutes)
Core Concepts
What are Interface Adapters?
Interface adapters are the layer that sits between your core application logic (use cases/entities) and the outside world (UI, frameworks, databases). They convert data from a format convenient for the outer layer to a format convenient for the inner layer, and vice versa.
Real-World Analogy: Think of a translator who helps two people who speak different languages communicate. The translator (adapter) ensures each person receives information in a way they understand, without changing the meaning.
Why This Matters: By isolating data conversion, you can change your UI or data sources without affecting your core business logic.
Controllers and Presenters
- Controllers: Receive input from the UI or web, validate it, package it into a request model (DTO), and call the appropriate use case.
- Presenters: Take the output from a use case and format it for display in the UI (e.g., create a ViewModel).
- This separation allows for flexible, testable, and maintainable code.
Key Terminology
- Controller: Handles input, validation, and calls use cases
- Presenter: Formats output for the UI
- ViewModel: A data structure tailored for the UI
4. Detailed Explanation and Examples (25-30 minutes)
Understanding Interface Adapters in Clean Architecture
Interface adapters are the glue between your core logic and the outside world. They ensure that changes in the UI or data sources do not ripple into your business logic.
Practical Applications
- In a web app, a controller receives HTTP requests, validates input, and calls a use case.
- A presenter takes the use case’s output and prepares it for display in a web page or mobile app.
Common Misconceptions
- Misconception: Controllers should contain business logic. Reality: Controllers should only handle input, validation, and delegation to use cases.
- Misconception: Presenters are unnecessary if the UI is simple. Reality: Presenters help keep UI formatting concerns out of business logic and make future changes easier.
5. Live Coding Demonstration (20-25 minutes)
Example 1: Controller and Presenter in Java
// Controller
public class AddTaskController {
private AddTaskUseCase useCase;
private AddTaskPresenter presenter;
public AddTaskController(AddTaskUseCase useCase, AddTaskPresenter presenter) {
this.useCase = useCase;
this.presenter = presenter;
}
public void handle(String id, String description) {
AddTaskRequest request = new AddTaskRequest();
request.id = id;
request.description = description;
AddTaskResponse response = useCase.execute(request);
presenter.present(response);
}
}
// Presenter
public class AddTaskPresenter {
public void present(AddTaskResponse response) {
if (response.success) {
System.out.println("Task added: " + response.message);
} else {
System.out.println("Error: " + response.message);
}
}
}
Explanation:
- The controller receives input, creates a request DTO, and calls the use case.
- The presenter formats the response for display (in this case, prints to console).
Example 2: ViewModel for the UI
// ViewModel tailored for the UI
public class TaskViewModel {
public String id;
public String description;
public String status;
}
// Presenter creates a ViewModel
public class TaskPresenter {
public TaskViewModel toViewModel(Task task) {
TaskViewModel vm = new TaskViewModel();
vm.id = task.getId();
vm.description = task.getDescription();
vm.status = task.isCompleted() ? "Completed" : "Pending";
return vm;
}
}
Key Points:
- Presenters can create ViewModels that are easy for the UI to consume.
- This keeps formatting and display logic out of the core business logic.
Example 3: Validation in the Controller
public class AddTaskController {
// ...existing code...
public void handle(String id, String description) {
if (description == null || description.isEmpty()) {
System.out.println("Description cannot be empty.");
return;
}
// ...existing code...
}
}
Debugging Demonstration
Show a common error:
// Common mistake: Controller contains business logic
public class AddTaskController {
public void handle(String id, String description) {
// Business logic here (should not be in controller!)
if (description.length() > 10) {
System.out.println("Task is too long");
}
// ...
}
}
// Corrected version: Business logic in use case
public class AddTaskUseCase {
public AddTaskResponse execute(AddTaskRequest request) {
if (request.description.length() > 10) {
AddTaskResponse response = new AddTaskResponse();
response.success = false;
response.message = "Task is too long";
return response;
}
// ...
}
}
6. Hands-On Practice (30-40 minutes)
Exercise 1: Basic Practice (10 minutes)
Objective: Implement a controller for marking a task as complete.
Instructions:
- Create a
CompleteTaskControllerclass. - The controller should receive an ID, validate it, and call the use case.
- Print the result using a presenter.
Starter Code:
public class CompleteTaskController {
// ...
}
Solution:
public class CompleteTaskController {
private CompleteTaskUseCase useCase;
private CompleteTaskPresenter presenter;
public CompleteTaskController(CompleteTaskUseCase useCase, CompleteTaskPresenter presenter) {
this.useCase = useCase;
this.presenter = presenter;
}
public void handle(String id) {
if (id == null || id.isEmpty()) {
System.out.println("ID cannot be empty.");
return;
}
CompleteTaskResponse response = useCase.execute(id);
presenter.present(response);
}
}
Exercise 2: Intermediate Challenge (15 minutes)
Objective: Implement a presenter that creates a ViewModel for the UI.
Requirements:
- Create a
TaskViewModelclass. - The presenter should convert a
Taskentity to aTaskViewModel. - The ViewModel should include ID, description, and status.
Hints:
- Use conditional logic to set the status (e.g., “Completed” or “Pending”).
Exercise 3: Creative Application (15 minutes)
Objective: Design controllers and presenters for a blog application (e.g., CreatePost, EditPost).
Challenge: Define the controller and presenter classes for creating and editing blog posts. Ensure controllers handle input/validation and presenters format output for the UI.
Extension Ideas:
- Add error handling and user-friendly messages
- Create ViewModels for different UI needs
7. Common Pitfalls and Troubleshooting (10 minutes)
Frequent Mistakes
-
Problem: Controllers contain business logic Cause: Not delegating to use cases Solution: Keep controllers focused on input/validation and delegation
-
Problem: Presenters are skipped, and UI formatting is done in use cases Cause: Mixing concerns Solution: Use presenters to format output for the UI
Debugging Tips
- Check for business logic in controllers or presenters
- Ensure presenters only format data, not process business rules
- Write unit tests for controllers and presenters
8. Best Practices and Professional Tips (5 minutes)
Industry Standards
- Keep controllers and presenters thin and focused
- Use ViewModels to tailor data for the UI
- Separate validation, business logic, and presentation concerns
Next Steps in Your Learning Journey
- Identify controllers and presenters in your own projects
- Refactor code to move formatting logic out of use cases
- Read about the Humble Object pattern for UI
9. Summary and Review (5 minutes)
Key Takeaways
- Interface adapters convert data between the UI/framework and core logic
- Controllers handle input and validation, presenters format output for the UI
- Separation of concerns improves maintainability and testability
Quick Review Questions
- What is the role of a controller in Clean Architecture?
- Why should presenters be used even for simple UIs?
- How do ViewModels help in separating concerns?
For Instructors
Teaching Tips
- Use real-world UI scenarios to illustrate controllers and presenters
- Encourage students to identify and refactor code that mixes concerns
- Review controller and presenter code for separation of responsibilities
Assessment Ideas
- Ask students to implement controllers and presenters for a sample domain
- Review code for correct use of ViewModels and formatting logic
- Peer review of controller and presenter implementations
Adaptation Notes
- For beginners, provide more scaffolding and examples
- For advanced students, challenge them to design adapters for complex UIs
- Adjust timing based on class experience and engagement