| ํญ๋ชฉ | ๋ด์ฉ |
| Invoke | /feature:data |
| Aliases | /data, /data:create |
| Category | petmedi-workflow |
| Complexity | standard |
| MCP Servers | serena, context7 |
/feature:data#
Context Framework Note: This behavioral instruction activates when Claude Code users type
/feature:data patterns.
Triggers#
- When a new Feature Data Layer is needed
- When Repository implementation, Serverpod Mixin, and Cache strategy are needed
/feature:create orchestration Step 4
Context Trigger Pattern#
/feature:data {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
)
|
--caching |
โ |
Caching strategy |
swr, cache-first, none (default: swr) |
Behavioral Flow#
1. Existing Pattern Analysis#
Serena MCP๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด Data Layer ํจํด ๋ถ์:
- feature/application/community/lib/src/data/repository/community_repository.dart
- feature/application/community/lib/src/data/repository/mixins/community_serverpod_mixin.dart
2. Generate Repository implementation#
import 'package:dependencies/dependencies.dart';
import 'package:serverpod_service/serverpod_service.dart';
import '../../domain/repository/i_{feature}_repository.dart';
import 'mixins/{feature}_serverpod_mixin.dart';
/// {Feature} Repository ๊ตฌํ์ฒด
@LazySingleton(as: I{Feature}Repository)
class {Feature}Repository
with {Feature}ServerpodMixin
implements I{Feature}Repository {
/// [{Feature}Repository]๋ฅผ ์์ฑํฉ๋๋ค.
{Feature}Repository(
this._serverpodService,
this._database,
);
final ServerpodService _serverpodService;
final {Feature}Database _database;
@override
ServerpodClient get client => _serverpodService.client;
@override
{Entity}Dao get {entity}Dao => _database.{entity}Dao;
}
3. Generate Serverpod Mixin (Namespace required!)#
import 'package:dependencies/dependencies.dart';
import 'package:serverpod_service/serverpod_service.dart' as pod; // โ
Namespace required
import '../../domain/entity/{entity}.dart';
import '../../domain/repository/i_{feature}_repository.dart';
/// {Feature} Serverpod API Mixin
mixin {Feature}ServerpodMixin implements I{Feature}Repository {
/// Serverpod client
pod.ServerpodClient get client; // โ
Using namespace
/// {Entity} DAO
{Entity}Dao get {entity}Dao;
@override
Future<Either<Failure, {Entity}>> create{Entity}({
required {Entity}Category category,
required String title,
required String content,
}) async {
try {
// 1. Domain โ Protocol ๋ณํ (๋ค์์คํ์ด์ค ์ฌ์ฉ)
final request = pod.{Entity}CreateRequest(
category: _categoryToProtocol(category),
title: title,
content: content,
);
// 2. API ํธ์ถ
final response = await client.{feature}.create{Entity}(request);
// 3. Protocol โ Entity ๋ณํ
final entity = _map{Entity}FromProtocol(response);
// 4. ์บ์์ ์ ์ฅ
await {entity}Dao.save{Entity}(entity);
return Right(entity);
} on Exception catch (error, stackTrace) {
return left(
RepositoryFailure(
'Failed to create {entity}',
error: error,
stackTrace: stackTrace,
),
);
}
}
// DTO ๋ณํ ํฌํผ
{Entity} _map{Entity}FromProtocol(pod.{Entity} response) {
return {Entity}(
id: response.id ?? 0,
title: response.title,
category: _categoryFromProtocol(response.category),
// ...
);
}
// Protocol โ Domain ๋ณํ
{Entity}Category _categoryFromProtocol(pod.{Entity}Category category) {
switch (category) {
case pod.{Entity}Category.qna:
return {Entity}Category.qna;
// ...
}
}
// Domain โ Protocol ๋ณํ
pod.{Entity}Category _categoryToProtocol({Entity}Category category) {
switch (category) {
case {Entity}Category.qna:
return pod.{Entity}Category.qna;
// ...
}
}
}
4. SWR Caching Pattern (GET List)#
// Repository Mixin์์ ์ ๋ต ์กฐ๋ฆฝ
@override
Stream<Either<Failure, List<{Entity}>>> get{Entity}s({
String? categoryId,
}) =>
SwrStrategyImpl<List<{Entity}>>(
cacheRepository: {Entity}DataCacheRepository(
{entity}ItemDao: {entity}ItemDao,
),
networkRepository: {Entity}DataNetworkRepository(
openApiService: openApiService,
),
policy: CachePolicies.standard,
).watchStream(
{Entity}DataCacheQuery(categoryId: categoryId),
);
5. CacheFirst Pattern (GET Single)#
@override
Future<Either<Failure, {Entity}?>> get{Entity}Detail({
required String id,
}) =>
CacheFirstStrategyImpl<{Entity}?>(
cacheRepository: {Entity}DetailCacheRepository(
{entity}DetailDao: {entity}DetailDao,
),
networkRepository: {Entity}DetailNetworkRepository(
openApiService: openApiService,
),
policy: CachePolicies.standard,
).execute(
{Entity}DetailCacheQuery(id: id),
);
Output Files#
feature/{location}/{feature_name}/lib/src/data/
โโโ repository/
โ โโโ {feature}_repository.dart
โ โโโ mixins/
โ โ โโโ {feature}_serverpod_mixin.dart
โ โโโ repository.dart # export
โโโ cache/
โ โโโ {entity}_cache_repository.dart
โโโ local/
โโโ tables/
โ โโโ {entity}_table.dart
โโโ dao/
โ โโโ {entity}_dao.dart
โโโ {feature}_database.dart
Post-Generation Commands#
# Code generation (Drift, Injectable)
melos exec --scope={feature_name} -- " dart run build_runner build --delete-conflicting-outputs "
MCP Integration#
- Serena: Existing Data Layer Pattern Analysis, Symbol search
- Context7: Serverpod, Drift Document reference
Examples#
Create Post Data Layer#
/feature:data community Post --location application --caching swr
Create Chat Message Data Layer#
/feature:data chat Message --location application --caching cache-first
Reference Agents#
Detailed implementation rules in ~/.claude/commands/agents/data-layer-agent.md reference
Core Rules Summary#
โ
Critical Import Pattern#
// Repository ๊ตฌํ์ฒด
import 'package:dependencies/dependencies.dart';
// Mixin (๋ค์์คํ์ด์ค ํ์!)
import 'package:serverpod_service/serverpod_service.dart' as pod;
mixin {Feature}ServerpodMixin implements I{Feature}Repository {
pod.ServerpodClient get client; // โ
Using namespace
// Domain Entity ์ฌ์ฉ (๋ค์์คํ์ด์ค ์์)
{Entity}Category category = {Entity}Category.qna;
// Serverpod DTO ์ฌ์ฉ (๋ค์์คํ์ด์ค ์ฌ์ฉ)
pod.{Entity}Category apiCategory = pod.{Entity}Category.qna;
}
Caching Strategy Selection (details: /client-cache skill reference)#
| Strategy | Entry Point | Return Type | Suitable For |
| SWR |
watchStream() |
Stream<Either<Failure, T>> |
GET lists, externally mutable |
| CacheFirst |
execute() |
Future<Either<Failure, T>> |
GET single, offline first |
| NetworkFirst |
execute() |
Future<Either<Failure, T>> |
Payment/auth, always need latest |