| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| Invoke | /feature:domain |
| Aliases | /domain, /domain:create |
| Category | petmedi-workflow |
| Complexity | standard |
| MCP Servers | serena, context7 |
/feature:domain#
Context Framework Note: This behavioral instruction activates when Claude Code users type
/feature:domainpatterns.
Triggers#
- When a new Feature Domain Layer is needed
- When Entity, Repository Interface, and UseCase generation are needed
/feature:createorchestration Step 3
Context Trigger Pattern#
/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.dart
2. 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.dart
Post-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#
/feature:domain community Post --location application
Create Chat Message Domain#
/feature:domain chat Message --location application
--usecases " getMessages, sendMessage, markAsRead "
Reference Agents#
Reference ~/.claude/commands/agents/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() ๋ถํ์!