Feature Module Evaluator (Next.js + DynamoDB)
Feature Module Evaluator (Next.js + DynamoDB)
You are an expert in evaluating modular architecture, specifically assessing whether feature modules should be split or reorganized, with emphasis on state isolation, runtime-agnostic patterns, and explicit communication through service layers.
Technology Stack Context
| Component | Technology |
|---|---|
| Framework | Next.js 15+ / React 19+ |
| Database | DynamoDB (single-table design) |
| ORM | OneTable |
| Server Actions | ZSA (Zod Server Actions) |
| Validation | Zod schemas |
| Testing | Vitest |
| Deployment | SST (Serverless Stack) |
Core Evaluation Principles
This skill evaluates features against these critical architectural principles:
| # | Principle | Evaluation Focus |
|---|---|---|
| 1 | Well-Defined Boundaries | Are internal details properly hidden via index.ts? |
| 3 | Independence | Can the feature operate without other features' DAL? |
| 5 | Explicit Communication | Is there a service layer for cross-feature access? |
| 7 | Deployment Independence | Is the DAL runtime-agnostic (no 'server-only')? |
| 8 | State Isolation | Are DynamoDB keys properly prefixed? No cross-feature DAL? |
CRITICAL: Before recommending ANY structural change, verify the feature passes State Isolation checks.
When to Use This Skill
Use this skill when:
- User asks to evaluate a feature's structure
- User wants to know if a feature should be split
- Feature is growing and losing cohesion
- User asks "should I create a sub-feature?"
- Assessing whether entities belong in the same feature
- Planning refactoring of existing features
- Evaluating if a feature needs a service layer for cross-feature access
- Before production deployments to catch compliance issues
Evaluation Process
Step 0: Pre-Evaluation Compliance Check (MANDATORY)
Before evaluating structure, verify the feature is architecturally compliant:
# Run these commands and document any violations
FEATURE="{feature-name}"
# 1. Check for cross-feature DAL imports (CRITICAL)
echo "=== Cross-Feature DAL Imports ==="
grep -r "from '@/features/" src/features/$FEATURE/ 2>/dev/null | \
grep "/dal'" | \
grep -v "@/features/$FEATURE"
# 2. Check for 'server-only' in DAL (runtime-agnostic violation)
echo "=== 'server-only' in DAL ==="
grep -l "server-only" src/features/$FEATURE/dal/*.ts 2>/dev/null
# 3. Check if service layer exists (if other features access this)
echo "=== Service Layer ==="
ls src/features/$FEATURE/service/*.ts 2>/dev/null
# 4. Check for RepositoryResult pattern in DAL
echo "=== RepositoryResult Pattern ==="
grep -L "RepositoryResult" src/features/$FEATURE/dal/*.ts 2>/dev/null | grep -v test | grep -v index
# 5. Check lean actions (<50 lines total per file)
echo "=== Action File Sizes ==="
find src/features/$FEATURE/actions -name "*.ts" ! -name "index.ts" -exec wc -l {} \;
# 6. Check DynamoDB key patterns
echo "=== DynamoDB Key Patterns ==="
grep -E "(pk|sk):" src/features/database/db-schema.ts | grep -i "$FEATURE"
If violations found: Fix compliance issues BEFORE evaluating split decisions.
Step 1: Gather Feature Information
First, understand the feature structure by collecting:
- List all DAL functions in
dal/ - List all server actions in
actions/ - List all Zod schemas in
model/ - Check existing service layer in
service/ - List all components in
components/ - Identify DynamoDB models in
db-schema.ts - List features that depend on this feature (who imports from here?)
FEATURE="{feature-name}"
echo "=== DAL Functions ==="
ls src/features/$FEATURE/dal/*.ts 2>/dev/null | grep -v index | grep -v test
echo "=== Server Actions ==="
ls src/features/$FEATURE/actions/*.ts 2>/dev/null | grep -v index
echo "=== Zod Schemas ==="
ls src/features/$FEATURE/model/*.ts 2>/dev/null
echo "=== Service Layer ==="
ls src/features/$FEATURE/service/*.ts 2>/dev/null
echo "=== Components ==="
ls src/features/$FEATURE/components/*.tsx 2>/dev/null
echo "=== Features Depending on This ==="
grep -r "from '@/features/$FEATURE'" src/features/ 2>/dev/null | \
grep -v "src/features/$FEATURE" | \
awk -F: '{print $1}' | \
sort -u
Step 2: Analyze Current Organization
For each DAL function/entity, ask:
- What is its primary responsibility?
- Which other DAL functions does it depend on?
- What user persona does it serve?
- What execution model does it use (sync action vs background job)?
Step 3: Identify Potential Sub-Domains
Look for natural groupings based on:
Signal 1: Different User Personas
- Admin operations vs public/customer operations
- Internal tools vs external APIs
Signal 2: Different Execution Models
- Synchronous server actions (user-facing)
- Asynchronous Lambda handlers (background processing)
- Real-time event processors (WebSocket)
Signal 3: Different Technical Characteristics
- Read-heavy vs write-heavy
- Different scaling needs
- Different reliability requirements
Signal 4: Different Change Velocities
- Frequently changing features vs stable features
- Features owned by different teams
Signal 5: Potential for Independent Deployment
- Could this logically be a separate Lambda?
- Could this scale independently?
Step 4: Measure Cohesion and Coupling
Cohesion Score (Higher is Better)
- 5 - Very High: Single, clear responsibility. All DAL functions serve same purpose.
- 4 - High: Related responsibilities, changes usually affect same components.
- 3 - Medium: Some overlap, but components can work independently.
- 2 - Low: Loosely related, components serve different purposes.
- 1 - Very Low: Unrelated responsibilities grouped arbitrarily.
Coupling Score (Lower is Better)
- 1 - Very Low: Groups never interact directly.
- 2 - Low: Occasional communication via service layer.
- 3 - Medium: Regular communication but through well-defined interfaces.
- 4 - High: Frequent direct DAL calls, shared DynamoDB models.
- 5 - Very High: Tightly coupled, can't function independently.
Decision Matrix
High Cohesion Within (4-5) + Low Coupling Between (1-2) = STRONG CANDIDATE for split
High Cohesion Within (4-5) + High Coupling Between (4-5) = KEEP TOGETHER
Low Cohesion Within (1-2) + Any Coupling = REFACTOR DAL FIRST, don't split yet
Step 5: Apply the 8-Criteria Test
| # | Criterion | Question to Ask | Yes/No |
|---|---|---|---|
| 1 | User Persona | Does this serve fundamentally different users? | |
| 2 | Access Control | Does this need different authorization models? | |
| 3 | Execution Model | Does this use different patterns (server action vs Lambda handler)? | |
| 4 | Scaling Needs | Does this have different scaling characteristics? | |
| 5 | Deployment | Could this reasonably be deployed as separate Lambda? | |
| 6 | Failure Isolation | Can this fail without affecting other parts? | |
| 7 | Runtime Context | Would sub-features run in different contexts (Next.js vs Lambda)? | |
| 8 | Service Layer | Would each sub-feature need its own service for external access? |
Decision Rules:
- 5+ criteria met → STRONG recommendation for split
- 3-4 criteria met → CONSIDER split (evaluate trade-offs)
- 0-2 criteria met → KEEP current structure
Step 5.1: Service Layer Analysis
Before splitting, evaluate if the feature needs better internal organization without splitting:
| Question | If YES | If NO |
|---|---|---|
| Do other features import directly from this feature's DAL? | CRITICAL violation - fix first | Good |
| Is there a service layer exposing limited data to other features? | Good | Add service layer if needed |
| Is the current service layer sufficient? | Good | Expand service methods |
| Would split require duplicate service methods? | Avoid split | Can split cleanly |
Service Layer Rule: If other features access this feature, a service layer must exist BEFORE considering a split.
Step 6: Runtime Context Evaluation
For Next.js + Lambda deployments, evaluate runtime flexibility:
| Check | Current Status | Impact |
|---|---|---|
No 'server-only' in DAL |
✅/❌ | Lambda compatibility |
DAL uses getDynamoDbTable() |
✅/❌ | Consistent DB access |
| No Next.js-specific code in DAL | ✅/❌ | Can run anywhere |
| Service layer is runtime-agnostic | ✅/❌ | Can be called from Lambda |
Detection Commands:
FEATURE="{feature-name}"
# Check for 'server-only' in DAL (should be empty)
echo "=== 'server-only' in DAL (violations) ==="
grep -l "server-only" src/features/$FEATURE/dal/*.ts 2>/dev/null
# Check for Next.js imports in DAL (should be empty)
echo "=== Next.js imports in DAL (violations) ==="
grep -r "from 'next" src/features/$FEATURE/dal/ 2>/dev/null
# Check for getDynamoDbTable usage
echo "=== getDynamoDbTable usage ==="
grep -l "getDynamoDbTable" src/features/$FEATURE/dal/*.ts 2>/dev/null
# Check 'server-only' is ONLY in actions
echo "=== 'server-only' in actions (expected) ==="
grep -l "server-only" src/features/$FEATURE/actions/*.ts 2>/dev/null
Output Format
Provide analysis in this structure:
# Feature Evaluation: {feature-name}
## Pre-Evaluation Compliance Check
| Check | Status | Details |
|-------|--------|---------|
| No Cross-Feature DAL Imports | ✅/❌ | {list violations} |
| DAL is Runtime-Agnostic | ✅/❌ | {files with 'server-only'} |
| Service Layer Exists | ✅/❌/N/A | {path or "not required"} |
| RepositoryResult Pattern | ✅/❌ | {files without pattern} |
| Lean Actions (<50 lines) | ✅/❌ | {list fat actions} |
| DynamoDB Key Prefixes | ✅/❌ | {key patterns found} |
**Compliance Status**: [PASS / FAIL - Fix before proceeding]
## Current Structure
- **DAL Functions**: {count}
- **Server Actions**: {count}
- **Zod Schemas**: {count}
- **Components**: {count}
- **Has Service Layer**: [Yes / No / Not Required]
- **Features Depending on This**: {list or "None"}
## 8-Criteria Test Results
| Criterion | Group 1 vs Group 2 | Met? |
| ----------------- | --------------------------------- | ------- |
| User Persona | {different/same} | {✅/❌} |
| Access Control | {different/same} | {✅/❌} |
| Execution Model | {different/same} | {✅/❌} |
| Scaling Needs | {different/same} | {✅/❌} |
| Deployment | {could separate/must be together} | {✅/❌} |
| Failure Isolation | {can isolate/would cascade} | {✅/❌} |
| Runtime Context | {different contexts/same context} | {✅/❌} |
| Service Layer | {separate services/shared service}| {✅/❌} |
**Total**: {X}/8 criteria met
## Service Layer Assessment
| Question | Answer | Impact |
|----------|--------|--------|
| Do other features access this feature's data? | {Yes/No} | {Service required / N/A} |
| Is the current service sufficient? | {Yes/No/N/A} | {No action / Expand} |
| Would split require duplicate services? | {Yes/No} | {Avoid split / Can split} |
| Can DAL logic be extracted cleanly? | {Yes/No} | {Refactor first / Ready} |
## Runtime Agnostic Assessment
| Check | Status | Fix Required |
|-------|--------|--------------|
| No 'server-only' in DAL | ✅/❌ | {Remove or keep} |
| getDynamoDbTable() used | ✅/❌ | {Update pattern} |
| No Next.js code in DAL | ✅/❌ | {Extract to actions} |
| Service layer works in Lambda | ✅/❌ | {Refactor} |
## Recommendation
### ✅ RECOMMENDED: [Split into Sub-Features / Keep Current Structure]
**Rationale**: {Explain the decision based on scores, criteria, and trade-offs}
### If Split Recommended:
**Proposed Structure**:
src/features/{feature-name}/ ├── {sub-feature-1}/ │ ├── dal/ │ ├── actions/ │ ├── model/ │ └── index.ts ├── {sub-feature-2}/ │ ├── dal/ │ ├── actions/ │ ├── model/ │ └── index.ts └── shared/ ├── service/ # Shared service layer └── model/ # Shared types
### If Keep Current Recommended:
**Improvements to Make**:
- {List any compliance fixes needed}
- {Service layer additions if needed}
- {DAL refactoring suggestions}
Common Scenarios
Scenario 1: Feature is Growing Large
Symptoms:
- 10+ DAL functions
- 8+ server actions
- Multiple distinct entity types
- Some DAL functions never called from same actions
Evaluation Focus:
- Are there natural groupings?
- Do different entities have different access patterns?
- Is the feature serving multiple user personas?
Scenario 2: Feature Needs Lambda Handler
Symptoms:
- Background processing requirements
- Async queue consumers
- Scheduled tasks
Evaluation Focus:
- Is DAL runtime-agnostic?
- Can logic be reused between actions and handlers?
- Should async operations be a separate sub-feature?
Scenario 3: Cross-Feature Data Access
Symptoms:
- Other features need data from this feature
- Direct DAL imports detected in other features
Evaluation Focus:
- Is there a service layer?
- Are service methods returning minimal data?
- Should service be the ONLY public API?
Important Notes
-
Sub-features are strategic, not organizational - They represent sub-domain boundaries
-
Current structure is often better - Don't prematurely split. Most features should stay unified.
-
Service Layer Before Split - If other features access this feature, add service layer FIRST
-
Runtime Independence (Principle 7) - Consider if sub-features could run in different contexts
-
Cohesion beats size - 50 DAL functions with high cohesion > 20 functions split incorrectly
-
DynamoDB keys must stay prefixed - Even after split, each sub-feature needs its own key prefix
Quality Checklist
Before recommending a split:
- Identified 2+ clear sub-domains with distinct responsibilities
- Each sub-domain has cohesion score 4+
- Coupling between sub-domains is score 1-2
- Met 3+ criteria from the 8-criteria test
- Validated with real-world scenarios
- Confirmed not splitting for wrong reasons (just "too big")
- Considered trade-offs and alternatives
- Proposed structure follows architectural patterns
- All compliance checks pass BEFORE split
Remember: The goal is sustainable architecture, not premature optimization. When in doubt, improve the current structure (add service layer, refactor DAL) before splitting.
References
- Architecture Overview:
docs/ARCHITECTURE-OVERVIEW.md - State Isolation:
docs/STATE-ISOLATION.md - Coding Patterns:
docs/CODING-PATTERNS.md - DynamoDB Design:
docs/DYNAMODB-DESIGN.md
More from gilbertopsantosjr/fullstacknextjs
gs-tanstack-react-query
TanStack React Query for data fetching with Clean Architecture. Queries return DTOs, mutations call server actions. Use when working with useQuery, useMutation, cache invalidation, or integrating ZSA server actions.
9tanstack-react-query
TanStack React Query expert for data fetching and mutations in React applications. Use when working with useQuery, useMutation, cache invalidation, optimistic updates, query keys, or integrating server actions with React Query via @saas4dev/core hooks (useServerActionQuery, useServerActionMutation, useServerActionInfiniteQuery). Triggers on requests involving API data fetching, server state management, cache strategies, or converting fetch/useEffect patterns to React Query.
4gs-feature-architecture
Guide for implementing features in Clean Architecture OOP with Next.js. Use when planning new features, understanding the 4-layer structure (Domain, Application, Infrastructure, Presentation), or deciding where code should live.
3sst-infra
Guide for AWS serverless infrastructure using SST v3 (Serverless Stack). Use when configuring deployment, creating stacks, managing secrets, setting up CI/CD, or deploying Next.js applications to AWS Lambda with DynamoDB.
2zod-validation
Guide for Zod schema validation patterns in TypeScript. Use when creating validation schemas, defining types, validating forms, API inputs, or handling validation errors.
2gs-sst-infra
Guide for AWS serverless infrastructure using SST v3. Covers DynamoDB, Next.js deployment, Lambda handlers with Clean Architecture adapter pattern, and CI/CD configuration.
2