AI-Bootstrapping for New Microservice
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
- Define OpenAPI (Swagger) specifications
- 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.
- Schema (i.e. data definition)
- Repository (i.e. lean CRUD operations with transaction management)
- Service (i.e. main business logic, coordinating repository & calling clients for external API calls)
- 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
- Router receives DTO
- Router passes DTO to Service
- Service starts Unit of Work (UoW)
- Service converts DTO to Domain Object
- Service uses Repository (via UoW) to persist Domain Object
- UoW commits the transaction
Roadmap for creating new micro-service [ Production-Ready ]
-
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
- List problem statement, API specifications, user-flow diagram
-
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
- Assess the complexity of business logic of the endpoint(s)
-
Development
- Add design patterns
- Unit of Work interface
- Client interface
- Add API layers
- Define Domain Objects (Entity)
- Add API contracts with
- Add Service layer with no-op
- Add repository layer (i.e. CRUD, Data Model)
- Add DB migration tool
- Add design patterns
-
Connect database
-
Deploy micro-service with relevant infrastructure (i.e. Redis dependency)