LogoSkills

feature:domain

Clean Architecture Domain Layer generation

ํ•ญ๋ชฉ๋‚ด์šฉ
Invoke/feature:domain
Aliases/domain, /domain:create
Categorypetmedi-workflow
Complexitystandard
MCP Serversserena, context7

/feature:domain#

Context Framework Note: This behavioral instruction activates when Claude Code users type /feature:domain patterns.

Triggers#

  • When a new Feature Domain Layer is needed
  • When Entity, Repository Interface, and UseCase generation are needed
  • /feature:create orchestration Step 3

Context Trigger Pattern#

/feature:domain {feature_name} {entity_name} [--options]

Parameters#

ParameterRequiredDescriptionExample
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#

  1. Entity extends Equatable
  2. Repository Interface uses I prefix
  3. UseCase uses Optional Constructor Injection (const GetPostsUsecase([IRepo? repo]))
  4. UseCase creates ConcreteRepo directly as default value (repo ?? const ConcreteRepo())
  5. 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() ๋ถˆํ•„์š”!