Context

I’ve implemented several new micro-services from the ground-up, this is to document my learning outcomes, good to have in the future!

Learning Outcomes

*Agent-first Development

Interestingly, while working on J4C benchmarking tool, we got access to Claude Code & started experimenting it to create the micro-service. Kinda cool but code got bloated really quickly and could result in technical debt 👀 Decided to follow the said patterns, let’s evaluate how it goes…

  • Documentation (i.e. ADR > Refinement > Plan/ Tasks)
  • Coding Agent (i.e. Claude Code, Cline) with memory & context management

Always start with Interfaces & Data Models before implementation

  1. Define OpenAPI (Swagger) specifications
  2. Useful to allow FE to work against a mock endpoint while BE works on the implementation

Always use Repository & Service Pattern Client interface for external API calls

One of my first J4C project-service, I added db_connector interface which was redundant LOL resulted in unnecessary boilerplate code.

  1. Schema (i.e. data definition)
  2. Repository* (i.e. lean CRUD operations with transaction management)
  3. Service (i.e. main business logic, coordinating repository & calling clients for external API calls)
  4. Router (i.e. catches & handles HTTP exceptions & calls services)

Use Unit of Work pattern between Service & multiple Repositories

Serves a Transaction Manager by ensuring all repository actions are grouped together (i.e. atomicity)

  • Ensures that updates to multiple repositories occur in a single transaction.
  • Used to manage commit/ rollbacks

Domain Models vs DTOs

Prevents DB structure from leaking to API consumers

  • Service layer interacts using Domain Objects (Entity)
    • User entity
  • Router/ API layer interacts using Schema/ DTOs (i.e. Data Transfer Object)
    • UserCreateRequest & UserCreateResponse

Always use Dependency Injection Pattern

  • Useful for injecting shared resources (i.e. API clients, DB sessions)
  • Able to plug-and-play with alternative Providers
  • Centralised logic for retry logic
    • Testability without tightly coupled code (i.e. able to mock in-memory database)

Must have Observability & Resilience

  • Ensure health check & ready endpoints
  • Structured logging & tracing with correlation_id
  • Ensuring idempotent outcomes during retry calls

Summary of Architectural Flow

  1. Router receives DTO
  2. Router passes DTO to Service
  3. Service starts Unit of Work (UoW)
  4. Service converts DTO to Domain Object
  5. Service uses Repository (via UoW) to persist Domain Object
  6. UoW commits the transaction

Roadmap for creating new micro-service [ Production-Ready ]

  1. Define agentic rules & project context definitions

    • List problem statement, API specifications, user-flow diagram
      • Always use AsyncSession, No logic in Routers
    • Define development workflow
      • Define requirements with human-in-the-loop (HIL) review
      • Add code changes (Vibe-coding + YOLO mode)
      • Testing
      • Linting & Formatting
      • Create Pull Request
  2. Scoping of Pull Requests/ Development Workflow

    • Assess the complexity of business logic of the endpoint(s)
      • Simple: CRUD operations primarily for interacting with database
      • Complex: Complex integration with different clients, heavy business logic
    • Decide the scope of each PR
      • Simple: Group correlated changes into single PR (i.e. CRUD endpoints)
      • Complex: Add heavy code changes & logic into independent PR
    • Add unit tests
    • Ensure passing tests, lint & formatting applied
  3. Development

    1. Add design patterns
      • Unit of Work interface
      • Client interface
    2. Add API layers
      • Define Domain Objects (Entity)
      • Add API contracts with
      • Add Service layer with no-op
    3. Add repository layer (i.e. CRUD, Data Model)
    4. Add DB migration tool
  4. Connect database

  5. Deploy micro-service with relevant infrastructure (i.e. Redis dependency)