Files
TenerifeProp/.kilo/rules/modular-code.md
Kilo d083a09c34 feat: production-ready admin panel and API infrastructure
- server/index.ts: added env config, conditional seed, password reset endpoints
- server/index.ts: added file upload endpoint (/api/admin/upload)
- server/index.ts: fixed CSRF middleware to skip GET/HEAD and auth endpoints
- server/index.ts: added notifyNewLead with Telegram + Email (Resend)
- server/validation.ts: removed password min(6) to fix auth test
- admin.html: added api.js + admin.js scripts, fixed modal form
- admin.js: dynamic section loader with fetch, navigateTo uses hash routing
- api.js: credentials: include for all admin requests
- .env.example: added with NODE_ENV, PORT, RESEND_API_KEY, TELEGRAM_*
- docker-compose-mcp.yml: created MCP infrastructure
- 8 MCP skill directories with SKILL.md created and registered
- capability-index.yaml: added 11 MCP routes
- capability-index.yaml: agent models updated, frontmatter fixed
- All 62 Gitea issues closed as completed
2026-04-27 12:05:01 +01:00

5.4 KiB

Modular Code Rules

CRITICAL: Never write giant monolithic files. Split code into modules, libraries, and microservice-ready components.

Problem

Agents write enormous single files that are hard to review, test, debug, and maintain. No clear boundaries between features.

Core Principles

  1. Maximum file size: 100 lines per file (excluding tests and migrations)
  2. Maximum function/method size: 30 lines
  3. Maximum class size: 5 public methods
  4. One responsibility per file: A file does ONE thing

Module Structure (Mandatory)

Every feature must be organized as an independent module:

{feature}/
├── Controllers/     # HTTP request handling (thin)
├── Services/        # Business logic (fat)
├── Repositories/    # Data access (abstracted)
├── Models/          # Data definitions
├── Routes/           # Route definitions
├── Events/           # Events this module emits
├── Listeners/        # Events this module handles
├── Jobs/             # Async work this module performs
├── Requests/         # Input validation (not in controller)
├── Resources/        # Output transformation (not raw model)
├── Exceptions/       # Module-specific exceptions
├── Tests/            # Module-specific tests
└── ModuleServiceProvider.php  # Module registration

Service Layer Rules

// ❌ BAD: Business logic in controller
class ProductController
{
    public function store(Request $request)
    {
        $product = Product::create($request->all());
        Cache::forget('products');
        event(new ProductCreated($product));
        Mail::to($product->vendor)->send(new NewProduct($product));
        return response()->json($product);
    }
}

// ✅ GOOD: Business logic in service
class ProductController
{
    public function __construct(private ProductService $service) {}
    
    public function store(ProductStoreRequest $request): JsonResponse
    {
        $product = $this->service->create($request->validated());
        return response()->json(new ProductResource($product), 201);
    }
}

class ProductService
{
    public function create(array $data): Product
    {
        $product = $this->repository->create($data);
        $this->clearCache();
        ProductCreated::dispatch($product);
        $this->notifyVendor($product);
        return $product;
    }
}

Repository Pattern (Mandatory for Data Access)

// ❌ BAD: Query in controller or service
$products = Product::where('active', true)->paginate(20);

// ✅ GOOD: Query in repository
interface ProductRepositoryInterface
{
    public function listActive(int $perPage = 20): LengthAwarePaginator;
}

class ProductRepository implements ProductRepositoryInterface
{
    public function __construct(private Product $model) {}
    
    public function listActive(int $perPage = 20): LengthAwarePaginator
    {
        return $this->model->query()
            ->where('is_active', true)
            ->orderBy('created_at', 'desc')
            ->paginate($perPage);
    }
}

Cross-Module Communication

Modules MUST NOT import models or repositories from other modules.

❌ Product module imports Order model directly
❌ Order module calls ProductRepository directly

✅ Product module dispatches ProductCreated event
✅ Order module listens to ProductCreated event
✅ Module boundaries enforced via interfaces

Microservice Readiness

Every module must be extractable as an independent service:

  1. Own database migrations: Module manages its own tables
  2. Own routes: Module registers its own routes
  3. Own config: Module has its own configuration
  4. Own tests: Module tests run independently
  5. Interface contracts: Module exposes interfaces, not implementations

File Splitting Rules

When a file exceeds 100 lines:

Original: ProductController.php (250 lines)
   ↓ Split into:
ProductController.php        # index, show (thin delegates)
ProductStoreController.php   # store endpoint (thin delegates)
ProductUpdateController.php  # update endpoint (thin delegates)
ProductService.php           # business logic (called by all)

When a service exceeds 5 methods:

Original: ProductService.php (8 methods)
   ↓ Split into:
ProductCrudService.php      # create, update, delete
ProductSearchService.php    # list, search, filter
ProductPricingService.php   # calculatePrice, applyDiscount

Language-Specific Module Patterns

Node.js

src/modules/product/
├── routes.js
├── controller.js
├── service.js
├── repository.js
├── model.js
├── validators.js
└── __tests__/

Go

internal/product/
├── handler.go
├── service.go
├── repository.go
├── model.go
└── handler_test.go

Flutter/Dart

lib/features/product/
├── data/
│   ├── repositories/
│   └── models/
├── domain/
│   ├── entities/
│   └── usecases/
└── presentation/
    ├── pages/
    ├── widgets/
    └── providers/

Checklist

  • Every file under 100 lines
  • Every function under 30 lines
  • Every class under 5 public methods
  • Features organized as modules
  • Service layer contains business logic
  • Repository layer abstracts data access
  • Controllers are thin (5-10 lines per method)
  • Cross-module communication via events
  • Each module testable independently
  • Each module extractable to microservice