Wordloop Platform
Platform ServicesCore (Go)

Core Architecture Rules

Clean Architecture guidelines for Wordloop Core

Architecture Rules: Core Service (Go)

The wordloop-core service is the direct physical manifestation of our Service Architecture Principles. It strictly abides by Clean Architecture (Ports and Adapters) to protect business rules from infrastructure volatility.

All domain logic resides in internal/; external dependencies point inwards.

1. Layers & Dependency Flow

  • Domain (internal/core/domain): Pure Go. Zero dependencies. Defines entities, validation rules, and sentinel errors.
  • Gateways (internal/core/gateway): Interfaces (Contracts) defining external data access. Depends only on Domain.
  • Services (internal/core/service): Business logic and orchestration. Depends on Domain and Gateways.
  • Providers (internal/provider): Concrete implementations (Postgres, PubSub). Depends on Domain and Gateways.
  • Entrypoints (internal/entrypoints): HTTP routes, JWT middleware, OpenAPI mappings. Depends on Domain and Services.

2. Context & Dependency Injection

  • Context is King: context.Context MUST be the first parameter of every boundary function. This is critical for OpenTelemetry trace propagation.
  • Constructor Injection: Use NewService(...) functions returning concrete types while accepting interfaces. Avoid global singleton state to ensure code remains deterministic and testable.

3. Telemetry & Observation

  • OpenTelemetry: Every HTTP endpoint and background job must initialize a Root Span. Provider calls (e.g., executing a SQL query or publishing to Pub/Sub) must extract and cascade the span.
  • Logging: Use slog exclusively. Always inject trace_id and span_id dynamically from the current context.

4. Authentication & Security

  • Clerk Identity: Entrypoints must use the validated Clerk JWT middleware. Do not trust user IDs from requests; extract them from the authenticated context token.
  • Testing: Provider layer testing requires actual Postgres containers (testcontainers). We prioritize absolute database fidelity; thus, we interact directly with real instances rather than mocking external state.

5. Migrations

  • All database schema updates must be written as discrete .sql migration files in scripts/migrations/.
  • State migrations are immutable. Rely solely on the programmatic runner (./dev db migrate).

On this page