Agent skill
Three-Layer Architecture
Server → Service → Repository pattern for feature organization
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/three-layer-architecture
SKILL.md
Three-Layer Architecture
LivestockAI features follow a Server → Service → Repository pattern for clean separation of concerns.
Layer Structure
app/features/batches/
├── server.ts # Auth, validation, orchestration
├── service.ts # Pure business logic
├── repository.ts # Database operations
├── types.ts # TypeScript interfaces
└── index.ts # Public exports
Layer Responsibilities
| Layer | Responsibility | Side Effects |
|---|---|---|
| Server | Auth, validation, orchestration | Yes (auth, DB) |
| Service | Business logic, calculations | No (pure) |
| Repository | Database CRUD operations | Yes (DB) |
Server Layer (server.ts)
Handles authentication, input validation, and orchestrates service/repository calls:
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
import { validateBatchData } from './service'
import { insertBatch } from './repository'
import { AppError } from '~/lib/errors'
export const createBatchFn = createServerFn({ method: 'POST' })
.inputValidator(
z.object({
farmId: z.string().uuid(),
species: z.string().min(1),
initialQuantity: z.number().int().positive(),
}),
)
.handler(async ({ data }) => {
// 1. Auth
const { requireAuth } = await import('../auth/server-middleware')
const session = await requireAuth()
// 2. Business logic validation (service layer)
const validationError = validateBatchData(data)
if (validationError) {
throw new AppError('VALIDATION_ERROR', {
metadata: { error: validationError },
})
}
// 3. Database operation (repository layer)
const { getDb } = await import('~/lib/db')
const db = await getDb()
return insertBatch(db, data)
})
Service Layer (service.ts)
Pure functions with no side effects - easy to test:
import type { CreateBatchData } from './server'
import { multiply, toDbString } from '~/features/settings/currency'
/**
* Calculate total cost for a batch
* Pure function - no side effects
*/
export function calculateBatchTotalCost(
initialQuantity: number,
costPerUnit: number,
): string {
if (initialQuantity <= 0 || costPerUnit < 0) {
return toDbString(0)
}
return toDbString(multiply(initialQuantity, costPerUnit))
}
/**
* Validate batch data before creation
* Returns error message or null if valid
*/
export function validateBatchData(data: CreateBatchData): string | null {
if (data.initialQuantity <= 0) {
return 'Initial quantity must be greater than 0'
}
if (data.costPerUnit < 0) {
return 'Cost per unit cannot be negative'
}
return null
}
/**
* Calculate Feed Conversion Ratio
*/
export function calculateFCR(
totalFeedKg: number,
weightGainKg: number,
): number | null {
if (totalFeedKg <= 0 || weightGainKg <= 0) {
return null
}
return Math.round((totalFeedKg / weightGainKg) * 100) / 100
}
Repository Layer (repository.ts)
Database operations only - no business logic:
import type { Kysely } from 'kysely'
import type { Database } from '~/lib/db/types'
export interface BatchInsert {
farmId: string
species: string
initialQuantity: number
currentQuantity: number
status: 'active' | 'depleted' | 'sold'
}
export async function insertBatch(
db: Kysely<Database>,
data: BatchInsert,
): Promise<string> {
const result = await db
.insertInto('batches')
.values(data)
.returning('id')
.executeTakeFirstOrThrow()
return result.id
}
export async function getBatchById(db: Kysely<Database>, id: string) {
return db
.selectFrom('batches')
.selectAll()
.where('id', '=', id)
.executeTakeFirst()
}
export async function updateBatch(
db: Kysely<Database>,
id: string,
data: Partial<BatchInsert>,
) {
return db
.updateTable('batches')
.set({ ...data, updatedAt: new Date() })
.where('id', '=', id)
.execute()
}
Benefits
- Testability: Service layer is pure functions, easy to unit test
- Separation: Clear boundaries between auth, logic, and data
- Reusability: Repository functions can be shared across server functions
- Maintainability: Changes to one layer don't affect others
Related Skills
tanstack-start- Server function patternskysely-orm- Repository layer queriesproperty-testing- Testing service layer
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?