/cc-flutter:feature:domain — 기능의 "핵심 규칙층" 자동 생성#
| 항목 | 내용 |
|---|---|
| 실행 명령 | /cc-flutter:feature:domain |
| 분류 | 워크플로우 |
| 난이도 | ●●○ 보통 |
| MCP 서버 | serena, context7 |
한마디로#
앱 기능의 가장 안쪽 뼈대(핵심 규칙·데이터 모양·동작 정의) 를 자동으로 만들어 줍니다. 건물로 치면 인테리어나 외관 전에 먼저 세우는 철골 구조와 설계 도면에 해당하는 부분이에요.
누가·언제 쓰나요#
- 새로운 기능을 만들기 시작하면서 그 기능의 데이터 구조와 핵심 동작 규칙을 정의해야 할 때
- 게시글(Post), 메시지(Message)처럼 다룰 대상(Entity) 과 "목록 조회·생성·수정·삭제" 같은 동작(UseCase) 이 필요할 때
- 전체 기능을 한 번에 만드는
/cc-flutter:feature:create작업의 3번째 단계로 자동 호출됩니다
무엇을 해주나요#
기능의 도메인(핵심 규칙) 폴더 안에 필요한 파일들을 한 번에 만들어 줍니다.
- Entity — 다룰 대상의 데이터 모양 정의 (예:
post.dart) -
Repository Interface — 데이터를 가져오고 저장하는 "약속(규격)" 정의 (예:
i_community_repository.dart) - UseCase — "목록 조회·단건 조회·생성·수정·삭제" 같은 개별 동작 (예:
get_posts_usecase.dart) - 테스트 코드 — 각 동작이 제대로 작동하는지 검증하는 자동 테스트
- 그 외 실패 메시지·예외 처리용 파일과 묶음 export 파일까지 함께 정리됩니다
어떻게 쓰나요#
# 게시글(Post) 도메인 만들기
/cc-flutter:feature:domain community Post --location application
# 채팅 메시지(Message) 도메인을, 원하는 동작만 골라서 만들기
/cc-flutter:feature:domain chat Message --location application
--usecases " getMessages, sendMessage, markAsRead "
- 첫 번째 값은 기능 이름(community, chat 등), 두 번째 값은 대상 이름(Post, Message 등)입니다.
-
--location으로 어디에 만들지(application/common/console) 정할 수 있고, 기본값은application입니다. --usecases로 필요한 동작만 콤마로 나열해 골라서 만들 수 있습니다.
안에서 무슨 일이 벌어지나요#
- 기존 패턴 살펴보기 — 이미 만들어진 비슷한 코드를 분석해 같은 스타일로 맞춥니다.
- 데이터 모양(Entity) 만들기 — 다룰 대상의 항목들(제목, 작성자, 작성일 등)을 정의합니다.
- 데이터 약속(Repository Interface) 만들기 — 조회·생성·수정·삭제 같은 규격을 정합니다.
- 동작(UseCase) 만들기 — 각 기능 동작을 하나씩 코드로 작성합니다.
- 테스트 만들기 — 각 동작이 잘 작동하는지 검증하는 코드를 함께 생성합니다.
- 마무리 정리 — 코드 생성 명령을 돌려 자동 생성 파일들을 완성합니다.
⚙️ 상세 옵션·실행 명세 (개발자 / AI 에이전트용)
Triggers#
- When a new Feature Domain Layer is needed
- When Entity, Repository Interface, and UseCase generation are needed
/cc-flutter:feature:createorchestration Step 3
Context Trigger Pattern#
/cc-flutter:feature:domain {feature_name} {entity_name} [--options]Parameters#
| Parameter | Required | Description | Example |
|---|---|---|---|
feature_name | ✅ | Feature module name (snake_case) | community, chat |
entity_name | ✅ | Entity name (PascalCase) | Post, Message |
--location | ❌ | Location | application, common, console (default: application) |
--usecases | ❌ | To generate UseCase | "getList, get, create, update, delete" |
Behavioral Flow#
1. Existing Pattern Analysis#
Analyze existing Domain Layer patterns using Serena MCP:
- feature/application/community/lib/src/domain/entity/post.dart
- feature/application/community/lib/src/domain/repository/i_community_repository.dart
- feature/application/community/lib/src/domain/usecase/get_posts_usecase.dart2. Entity Generation#
import 'package:dependencies/dependencies.dart';
/// {엔티티} 한글 설명
class {Entity} extends Equatable {
/// [{Entity}]를 생성합니다.
const {Entity}({
required this.id,
required this.title,
required this.authorId,
required this.createdAt,
this.updatedAt,
});
final int id;
final String title;
final int authorId;
final DateTime createdAt;
final DateTime? updatedAt;
@override
List<Object?> get props => [id, title, authorId, createdAt, updatedAt];
}
3. Generate Repository Interface#
import 'package:dependencies/dependencies.dart';
/// {Feature} Repository Interface
abstract interface class I{Feature}Repository {
/// {엔티티} 목록을 SWR Stream으로 조회합니다.
Stream<Either<Failure, List<{Entity}>>> get{Entity}s({
{Entity}Category? category,
});
/// {엔티티} 단건 조회
Future<Either<Failure, {Entity}>> get{Entity}(int {entity}Id);
/// {엔티티} 생성
Future<Either<Failure, {Entity}>> create{Entity}({...});
/// {엔티티} 수정
Future<Either<Failure, {Entity}>> update{Entity}({...});
/// {엔티티} 삭제
Future<Either<Failure, void>> delete{Entity}(int {entity}Id);
}
4. Generate UseCase (Optional Constructor Injection)#
import 'package:core/core.dart';
import 'package:dependencies/dependencies.dart';
/// {엔티티} 목록 조회 Params
class Get{Entity}sParams {
const Get{Entity}sParams({
this.limit = 20,
this.offset = 0,
this.category,
});
final int limit;
final int offset;
final {Entity}Category? category;
}
/// {엔티티} 목록을 조회하는 UseCase
class Get{Entity}sUsecase {
/// [Get{Entity}sUsecase]를 생성합니다.
const Get{Entity}sUsecase([I{Feature}Repository? repo])
: _repo = repo ?? const {Feature}Repository();
final I{Feature}Repository _repo;
Future<Either<Failure, {Entity}ListResult>> call(Get{Entity}sParams params) {
return _repo.get{Entity}s(
limit: params.limit,
offset: params.offset,
category: params.category,
);
}
}
5. Generate UseCase tests#
void main() {
late Get{Entity}sUsecase usecase;
late MockI{Feature}Repository mockRepository;
setUpAll(registerFallbackValues);
setUp(() {
mockRepository = MockI{Feature}Repository();
usecase = Get{Entity}sUsecase(mockRepository); // 직접 주입
});
group('Get{Entity}sUsecase', () {
test('성공 시 Right({Entity}ListResult) 반환', () async {
// arrange
when(() => mockRepository.get{Entity}s(...))
.thenAnswer((_) async => Right(testResult));
// act
final result = await usecase(testParams);
// assert
expect(result.isRight(), true);
verify(() => mockRepository.get{Entity}s(...)).called(1);
});
});
}
Output Files#
feature/{location}/{feature_name}/lib/src/domain/
├── entity/
│ ├── {entity}.dart
│ ├── {entity}_list_result.dart
│ └── entity.dart # export
├── repository/
│ ├── i_{feature}_repository.dart
│ └── repository.dart # export
├── usecase/
│ ├── get_{entity}s_usecase.dart
│ ├── get_{entity}_usecase.dart
│ ├── create_{entity}_usecase.dart
│ ├── update_{entity}_usecase.dart
│ ├── delete_{entity}_usecase.dart
│ └── usecase.dart # export
├── failure/
│ └── {feature}_failure_messages.dart
└── exception/
└── {feature}_exception.dart
feature/{location}/{feature_name}/test/domain/usecase/
├── get_{entity}s_usecase_test.dart
├── get_{entity}_usecase_test.dart
├── create_{entity}_usecase_test.dart
├── update_{entity}_usecase_test.dart
└── delete_{entity}_usecase_test.dartPost-Generation Commands#
# Code generation (Freezed 등)
melos exec --scope={feature_name} -- " dart run build_runner build --delete-conflicting-outputs "MCP Integration#
- Serena: Existing Domain Layer Pattern Analysis, Symbol search
- Context7: Clean Architecture, Either Pattern Document reference
Examples#
Create Post Domain#
/cc-flutter:feature:domain community Post --location applicationCreate Chat Message Domain#
/cc-flutter:feature:domain chat Message --location application
--usecases " getMessages, sendMessage, markAsRead "Reference Agents#
Reference ${CLAUDE_PLUGIN_ROOT}/agents/app/domain-layer-agent.md for detailed implementation rules
Core Rules Summary#
✅ Required Patterns#
- Entity extends Equatable
- Repository Interface uses
Iprefix - UseCase uses Optional Constructor Injection (
const GetPostsUsecase([IRepo? repo])) - UseCase creates ConcreteRepo directly as default value (
repo ?? const ConcreteRepo()) - Write unit tests for all UseCases
❌ Prohibited Patterns#
// ❌ @injectable 어노테이션 금지
// @injectable
// class GetPostsUseCase { ... }
// ❌ Getting Repository via getIt prohibited
// IRepo get repo => getIt<IRepo>();
// ❌ Relative path import prohibited
// import '../entity/post.dart';
Test Patterns#
setUp(() {
mockRepository = MockI{Feature}Repository();
usecase = Get{Entity}sUsecase(mockRepository); // 직접 주입
});
// getIt.reset() 불필요!