Skip to content
My Guidelines
Guides
In this guide (4 items)

Package & Component Diagrams

Package & Component Diagrams

Project-agnostic catalog. Part of the Diagram Catalog. Covers: package dependency diagram, runtime component diagram, event flow diagram (transactional outbox).

Replace com.example.platform / platform-service placeholders with your project’s actual package and service name when applying patterns.


8. Architecture Diagrams

8.1 Package Dependency Diagram

┌─────────────────────────────────────────────────────────┐
│ BOOTSTRAP │
│ (Configuration) │
│ │
│ Depends on: ALL LAYERS │
└────┬────────────────────────────────────────────────┬───┘
│ │
│ wires wires│
│ │
┌────▼──────────────────────┐ ┌────────────────▼───┐
│ ENTRY POINTS │ │ DRIVEN ADAPTERS │
│ (Infrastructure) │ │ (Infrastructure) │
│ │ │ │
│ Depends on: │ │ Depends on: │
│ • Application (ports) │ │ • Application │
│ • Domain (entities) │ │ • Domain │
│ • Spring Boot │ │ • Spring Data │
│ • Jackson │ │ • Kafka │
└────┬──────────────────────┘ │ • Redis │
│ └────────┬───────────┘
│ implements │
│ inbound ports │ implements
│ │ outbound ports
│ │
┌────▼────────────────────────────────────────▼───────────┐
│ APPLICATION LAYER │
│ │
│ • Inbound Ports (interfaces) │
│ • Outbound Ports (interfaces) │
│ • Use Cases (business logic) │
│ • Commands (input DTOs) │
│ │
│ Depends on: Domain ONLY │
└────────────────────────┬─────────────────────────────────┘
│ uses
┌────────────────────────▼─────────────────────────────────┐
│ DOMAIN LAYER │
│ │
│ • Entities (Organization, User) │
│ • Enums (OrganizationStatus, Roles) │
│ • Exceptions (OrganizationNotFoundException) │
│ • Utils (MdcUtil, TraceContextUtil) │
│ │
│ Depends on: NOTHING (Pure Kotlin) │
└──────────────────────────────────────────────────────────┘

8.2 Runtime Component Diagram

┌──────────────────────────────────────────────────────────────┐
│ CLIENT │
│ (Browser/Mobile) │
└────────────────────┬─────────────────────────────────────────┘
│ HTTP Request
│
┌────────────────────▼─────────────────────────────────────────┐
│ SPRING BOOT │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ TraceIdFilter │ │
│ │ (Extract/Generate W3C Trace Context) │ │
│ └────────────────┬───────────────────────────────────────┘ │
│ │ │
│ ┌────────────────▼───────────────────────────────────────┐ │
│ │ OrganizationController │ │
│ │ • Validate request (@Validated) │ │
│ │ • Map DTO → Command │ │
│ │ • Invoke port.execute() │ │
│ │ • Map Domain → Response DTO │ │
│ └────────────────┬───────────────────────────────────────┘ │
└───────────────────┼──────────────────────────────────────────┘
│ CreateOrganizationPort.execute(command)
┌───────────────────▼──────────────────────────────────────────┐
│ USE CASE LAYER │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ CreateOrganizationUseCase │ │
│ │ • Map Command → Domain Entity │ │
│ │ • Orchestrate business flow │ │
│ │ • Manage transaction (Transaction port) │ │
│ │ • Append event to outbox — same transaction │ │
│ └───┬───────────────┬──────────────────┬───────────────┘ │
└──────┼───────────────┼──────────────────┼───────────────────┘
│ │ │
│ save() │ run { } │ outbox.save(event)
│ │ wraps both │
┌──────▼─────┐ ┌──────▼──────┐ ┌───────▼──────────────────┐
│ Repository │ │ Transaction │ │ Outbox JDBC │
│ Adapter │ │ Adapter │ │ Adapter │
│ │ │ │ │ (OutboxRepositoryPort) │
│ ┌────────┐ │ │ @Transact.. │ │ │
│ │ Cache │ │ │ │ │ INSERT INTO outbox │
│ │ Layer │ │ └─────────────┘ │ status = PENDING │
│ └───┬────┘ │ └─────────┬────────────────┘
│ ┌───▼────┐ │ │
│ │Metrics │ │ │
│ └───┬────┘ │ │
│ ┌───▼────┐ │ │
│ │ JDBC │ │ │
│ └───┬────┘ │ │
└─────┼──────┘ │
│ │
┌─────▼────────────────────────────────────▼───┐
│ PostgreSQL Database (one local transaction) │
│ • organizations table │
│ • outbox table │
└──────────────────────┬───────────────────────┘
│ claim PENDING → IN_FLIGHT
│ (FOR UPDATE SKIP LOCKED)
┌──────────────────────▼───────────────────────┐
│ Outbox Dispatcher (async, @SchedulerLock) │
│ SendOutboxMessagesUseCase │
│ → MessagePublisherPort (Kafka/Rabbit) │
│ → mark PUBLISHED | retry with backoff │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ Kafka Cluster (organization-created) │
└──────────────────────────────────────────────┘

The request path never talks to the broker. The use case’s only publishing act is an INSERT into the outbox inside the business transaction; the dispatcher owns the broker call, retries and DLT. See messaging.md §5.

8.3 Event Flow Diagram (Transactional Outbox)

Time →
─────────────────────────────────────────────────────────────
T0: Client Request Arrives
│
▼
┌───────────────────────────────────────────────────────────┐
│ HTTP POST /api/v1/organizations │
└───────────────────────────────────────────────────────────┘
│
T1: Transaction Begins
│
▼
┌───────────────────────────────────────────────────────────┐
│ BEGIN TRANSACTION │
│ │
│ 1. INSERT INTO organizations (...) │
│ VALUES (uuid, 'Acme', 'PENDING_APPROVAL', ...) │
│ │
│ 2. INSERT INTO outbox (...) │
│ VALUES (uuid, 'org-created', '{"id":"...", ...}', │
│ status = 'PENDING') │
│ │
│ COMMIT TRANSACTION ✓ (both rows or neither) │
│ │
│ ✗ No broker call here — not even a "try first". │
└───────────────────────────────────────────────────────────┘
│
T2: Response Sent
│
▼
┌───────────────────────────────────────────────────────────┐
│ HTTP 201 Created │
│ Location: /api/v1/organizations/{uuid} │
└───────────────────────────────────────────────────────────┘
⏰ Next dispatcher tick (seconds later)
T3: Dispatcher Claims and Publishes
│
▼
┌───────────────────────────────────────────────────────────┐
│ @SchedulerLock → SendOutboxMessagesUseCase │
│ (ShedLock: one instance ticks at a time, per service) │
│ │
│ 1. Claim batch atomically: │
│ UPDATE outbox SET status = 'IN_FLIGHT', │
│ claimed_at = NOW(), claimed_by = app@pid │
│ WHERE id IN (SELECT ... WHERE status = 'PENDING' │
│ OR stale IN_FLIGHT (> claim TTL) │
│ LIMIT 100 FOR UPDATE SKIP LOCKED) │
│ │
│ 2. For each claimed message: │
│ a. MessagePublisherPort.publish(...) │
│ b. ok → status = 'PUBLISHED', clear claim │
│ err → status = 'PENDING', next_attempt_at = │
│ now + backoff (FAILED after max attempts) │
└───────────────────────────────────────────────────────────┘
│
T4: Event Consumed
│
▼
┌───────────────────────────────────────────────────────────┐
│ Other Microservices (Consumers) │
│ • User Service → Creates default admin user │
│ • Notification Service → Sends welcome email │
│ • Analytics Service → Records new organization metric │
└───────────────────────────────────────────────────────────┘