
webapp
by tennashi
SKILL.md
name: webapp description: Generate web application code including HTTP handlers, repository interfaces, implementations, and tests. Use when building REST APIs, creating CRUD endpoints, scaffolding backend services, or implementing web applications.
Web Application Generator
Overview
This skill generates web application code from domain models using Clean Architecture. It applies sensible defaults that can be overridden by project-specific CLAUDE.md.
Workflow
Execute all steps in sequence without stopping for user confirmation.
-
Analyze Relationships
- Follow
analyze-relationsskill to analyze domain models - Do NOT write to CLAUDE.md, do NOT stop after this step
- Follow
-
Write Layer Structure
- Follow
analyze-layersskill to derive layers from requirements - Write
## Layer Structurewith Behaviors to CLAUDE.md, do NOT stop after this step
- Follow
-
Design Directory Structure
- Follow
design-structureskill to derive directory structure - Read
## Layer Structurefrom CLAUDE.md - Write
## Directory Structureto CLAUDE.md, do NOT stop after this step
- Follow
-
Generate Code
- Read CLAUDE.md for configuration, layer structure, and directory structure
- Output to
dist/directory (keeps source clean) - dist/ should be a complete, runnable application
-
Generate Tests
- Read Behaviors from
## Layer Structurein CLAUDE.md - Generate test code based on Behaviors (see Test Generation section)
- Output to
dist/directory alongside implementation
- Read Behaviors from
-
Verify
- Ensure generated code compiles
- Ensure tests pass
- Check for proper error handling
Layer Definition
| Condition | Layer Definition |
|---|---|
| Default, widely understood | Clean Architecture |
| Traditional enterprise | Layered |
| Emphasis on ports/adapters | Hexagonal |
| Domain model centric | Onion |
| Simple, fewer abstractions | Three-Tier |
Each reference contains layer concepts and Layer Structure Template for CLAUDE.md:
- Clean Architecture (default) - use
analyze-layersskill - Layered
- Hexagonal
- Onion
- Three-Tier
For non-Clean Architecture styles, copy Layer Structure Template from the reference file to CLAUDE.md.
Test Generation
Tests verify that each layer fulfills its Behavior (Precondition/Postcondition/Invariant).
Test Strategy by Layer Type
| Layer/Component | What to test | How to test |
|---|---|---|
| Entity (inner) | Postcondition: correct decisions based on rules | Unit test with various inputs |
| Entity (inner) | Invariant: business rules always satisfied | Unit test that invariant holds after any operation |
| UseCase | Postcondition: goal achieved by coordinating dependencies | Unit test with mocked dependencies |
| UseCase | Invariant: consistency maintained | Unit test that checks consistency after operations |
| Handler (input) | Postcondition: correct response for valid/invalid requests | Unit test with mocked inner layer |
| Repository (output) | Postcondition: save then retrieve returns equivalent data | Integration test with real or in-memory DB |
| Gateway (output) | Postcondition: correct external call made | Unit test with mocked external service |
Test File Structure
Place test files alongside implementation:
dist/
entity/
task.go
task_test.go # Entity tests
handler/
task_handler.go
task_handler_test.go # Handler tests
repository/
task_repository.go
task_repository_test.go # Repository tests
Test Patterns
Entity Test (Invariant + Postcondition)
func TestTask_CanTransitionTo(t *testing.T) {
// Postcondition: Returns correct decision based on business rules
task := NewTask("title", StatusTodo)
// Valid transition
if !task.CanTransitionTo(StatusInProgress) {
t.Error("should allow Todo -> InProgress")
}
// Invalid transition
if task.CanTransitionTo(StatusDone) {
t.Error("should not allow Todo -> Done directly")
}
}
func TestTask_Invariant(t *testing.T) {
// Invariant: Entity always satisfies business rules
task := NewTask("title", StatusTodo)
task.Complete()
if task.Status != StatusDone {
t.Error("completed task should have Done status")
}
if task.CompletedAt == nil {
t.Error("completed task should have CompletedAt set")
}
}
Handler Test (Postcondition)
func TestTaskHandler_Create(t *testing.T) {
// Postcondition: Valid request → correct inner call → correct response
mockUseCase := &MockTaskUseCase{}
handler := NewTaskHandler(mockUseCase)
req := httptest.NewRequest("POST", "/tasks", strings.NewReader(`{"title":"test"}`))
rec := httptest.NewRecorder()
handler.Create(rec, req)
if rec.Code != http.StatusCreated {
t.Errorf("expected 201, got %d", rec.Code)
}
}
func TestTaskHandler_Create_InvalidRequest(t *testing.T) {
// Postcondition: Invalid request → error response
handler := NewTaskHandler(&MockTaskUseCase{})
req := httptest.NewRequest("POST", "/tasks", strings.NewReader(`{invalid}`))
rec := httptest.NewRecorder()
handler.Create(rec, req)
if rec.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", rec.Code)
}
}
Repository Test (Postcondition)
func TestTaskRepository_SaveAndFind(t *testing.T) {
// Postcondition: Save then retrieve → equivalent data returned
db := setupTestDB(t)
repo := NewTaskRepository(db)
task := &Task{Title: "test", Status: StatusTodo}
err := repo.Save(context.Background(), task)
if err != nil {
t.Fatal(err)
}
found, err := repo.FindByID(context.Background(), task.ID)
if err != nil {
t.Fatal(err)
}
if found.Title != task.Title || found.Status != task.Status {
t.Error("retrieved task should be equivalent to saved task")
}
}
func TestTaskRepository_FindByID_NotFound(t *testing.T) {
// Postcondition: Retrieve non-existent → not-found indication
db := setupTestDB(t)
repo := NewTaskRepository(db)
_, err := repo.FindByID(context.Background(), "nonexistent")
if err != ErrNotFound {
t.Errorf("expected ErrNotFound, got %v", err)
}
}
Test Conventions
- Use table-driven tests for multiple input scenarios
- Name tests as
Test{Component}_{Method}orTest{Component}_{Behavior} - Comments should reference which Behavior (Precondition/Postcondition/Invariant) is being tested
- Use
t.Helper()for test helper functions - Use
t.Parallel()where safe
Defaults
These defaults apply unless overridden in project's CLAUDE.md.
API Design
| Relationship | Route Pattern |
|---|---|
| Top-level entity | /{entities}, /{entities}/{id} |
| belongs_to | /{parents}/{parentID}/{children} |
| Self-reference | /{entities}/{id}/sub{entities} |
| Many-to-many | /{entities}/{id}/{related}, /{entities}/{id}/{related}/{relatedID} |
| Polymorphic | Routes on each target (/{targets}/{id}/attachments) |
Conventions
- Use
context.Contextfor all repository methods - Return domain errors, not database-specific errors
- Use domain methods for business logic (e.g.,
entity.CanDelete(userID)) - Handler signature:
func(w http.ResponseWriter, r *http.Request) - JSON for request/response bodies
- User ID from
X-User-IDheader (for authorization checks)
Authorization
Infer authorization rules from domain methods:
CanDelete(userID)→ check before deleteCanEdit(userID)→ check before updateIsOwner(userID)→ owner-only operationsIsMember(userID)→ member-only access
Schema Generation
- Generate
initSchema()function in main.go - Include foreign key constraints based on relationships
- Add indexes for foreign key columns
- Use appropriate types per database
Project CLAUDE.md
Projects specify (human writes):
## Application
{Description of the application}
## External Interfaces
- {Name}: {Description}
## External Dependencies
- {Name}: {Description}
## Tech Stack
- Language: Go 1.21+
- HTTP Router: chi
- Database: SQLite with sqlx
Generated by skills (can be edited by human):
## Layer Structure
(Includes Behaviors for each layer/component)
## Directory Structure
(Derived by design-structure skill)
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です