Flutter Calculator 예제로 정리한 Clean Architecture 3계층 구조 (Presentation → Domain → Data) + 의존성 주입(DI) 노트
User ↔ View ↔ Presenter(Presentation) ↔ UseCase ↔ Repository(Domain) ↔ RepositoryImpl ↔ DataSource(Data)
| 계층 | 핵심 요소 | 역할 |
|---|---|---|
| Presentation | View, Presenter(ViewModel) | 사용자 입력을 받아 UseCase 호출, 상태 갱신 |
| Domain | Entity, Repository(interface), UseCase | 비즈니스 로직, 다른 계층에 의존하지 않는 순수 계층 |
| Data | Model, RepositoryImpl, DataSource | 실제 데이터 저장/조회 (로컬, 서버 등) |
의존성 방향은 항상 바깥 → 안쪽 (Presentation → Domain ← Data) 으로 향하며, Domain 계층은 다른 계층을 몰라야 하는 것이 핵심.
presentation/
└─ view_model/
├─ calculator.dart
└─ view_model.dart
└─ presentation.dart
class CalculatorViewModel extends ValueNotifier<Calculator> {
CalculatorViewModel(super.calculator);
Future<void> load() async {
final String result = await CalculatorDataSource(CalculatorLocalDataSource()).fetch();
value = Calculator(result: result);
}
Future<void> save() async {
await CalculatorDataSource(CalculatorLocalDataSource()).save(value.result);
}
void calculate(String buttonText) {
value.calculate(buttonText);
notifyListeners();
}
}
⚠️ 문제: Presenter(ViewModel)가 Data 계층(CalculatorDataSource)을 직접 참조 → 클린 아키텍처 위반 (Presentation이 Domain을 거치지 않고 Data에 직접 의존)
class FetchCalculatorUseCase {
Future<Calculator> execute() async {
final String value = await CalculatorDataSource(CalculatorLocalDataSource()).fetch();
return Calculator(result: value);
}
}
class SaveCalculatorUseCase {
Future<void> execute(Calculator value) {
return CalculatorDataSource(CalculatorLocalDataSource()).save(value.result);
}
}
class CalculatorViewModel extends ValueNotifier<Calculator> {
final FetchCalculatorUseCase _fetchCalculatorUseCase;
final SaveCalculatorUseCase _saveCalculatorUseCase;
CalculatorViewModel(
this._fetchCalculatorUseCase,
this._saveCalculatorUseCase,
super.calculator,
);
Future<void> load() async { /* ... */ }
Future<void> save() async { /* ... */ }
void calculate(String buttonText) { /* ... */ }
}
✅ ViewModel(Presenter)이 UseCase에만 의존하도록 변경 → Presentation ↔ UseCase 관계 확립
data/
├─ model/
│ ├─ calculator.dart
│ └─ model.dart
├─ repository/
│ ├─ calculator.dart
│ └─ repository.dart
├─ source/
│ ├─ local/
│ │ ├─ calculator.dart
│ │ └─ local.dart
│ ├─ calculator.dart
│ └─ source.dart
└─ data.dart