System Architecture Overview
SpecKit Ticketing Platform is built on a distributed microservices architecture that combines Hexagonal Architecture (Ports & Adapters), CQRS, and Event-Driven Design to create a scalable, maintainable ticketing system.Architecture Diagram
Core Architectural Principles
1. Hexagonal Architecture (Ports & Adapters)
1. Hexagonal Architecture (Ports & Adapters)
Each microservice is structured using hexagonal architecture, which isolates the domain logic from infrastructure concerns.Structure:Key Benefits:
- Domain logic remains pure and testable
- Easy to swap infrastructure implementations
- Clear separation of concerns
- Dependency inversion: Domain depends on abstractions, not implementations
2. Bounded Contexts
2. Bounded Contexts
The system is divided into distinct bounded contexts, each owning its data and business logic:
Context Independence:
| Bounded Context | Responsibility | Key Entities |
|---|---|---|
| Catalog | Event browsing, seat information | Event, Seat |
| Inventory | Seat reservations, availability | Reservation, Seat |
| Ordering | Shopping cart, order management | Order, OrderItem |
| Payment | Payment processing, validation | Payment, Transaction |
| Fulfillment | Ticket generation, delivery | Ticket |
| Identity | User authentication, authorization | User, Token |
| Notification | Email notifications | EmailNotification |
- Each context has its own database schema (e.g.,
bc_catalog,bc_inventory) - Services communicate via well-defined contracts
- No direct database sharing between contexts
3. Service Layers
3. Service Layers
Each microservice follows a consistent layering strategy:Layer Responsibilities:
- API Layer
- Application Layer
- Domain Layer
- Infrastructure Layer
Role: HTTP endpoints and request/response handling
Communication Patterns
Synchronous (REST)
When to Use:
- Immediate queries (browsing events)
- Real-time validation (seat availability)
- User-initiated actions requiring instant feedback
GET /api/events→ Catalog ServicePOST /api/reservations→ Inventory ServicePOST /api/cart/add→ Ordering Service
Asynchronous (Kafka)
When to Use:
- Long-running workflows (payment processing)
- Event choreography across services
- Eventual consistency scenarios
reservation-createdevent triggers order creationpayment-succeededevent triggers ticket fulfillmentticket-issuedevent triggers notification
Data Consistency Strategy
- Strong Consistency
- Eventual Consistency
Used for: Critical operations within a bounded contextImplementation:
- PostgreSQL transactions with ACID guarantees
- Unit of Work pattern via EF Core’s
SaveChangesAsync() - Redis distributed locks for cross-instance coordination
Technology Stack
| Component | Technology | Purpose |
|---|---|---|
| Backend | .NET 9, Minimal APIs | High-performance microservices |
| Messaging | MediatR | In-process command/query handling |
| ORM | Entity Framework Core | Database access with migrations |
| Event Bus | Apache Kafka | Asynchronous event choreography |
| Caching/Locking | Redis | Distributed locks, session state |
| Database | PostgreSQL | Persistent data storage (schema-per-service) |
| Observability | OpenTelemetry, Serilog | Distributed tracing and logging |
| Frontend | Next.js 14, TailwindCSS | Modern React-based UI |
Key Design Decisions
Why Hexagonal Architecture?
Why Hexagonal Architecture?
Benefits Realized:
-
Testability: Domain logic can be tested without infrastructure
-
Flexibility: Easy to swap implementations
- Production:
RedisLock→ StackExchange.Redis - Testing:
MockRedisLock→ In-memory simulation
- Production:
-
Clear Boundaries: Domain never references infrastructure types
Why Separate Database Schemas?
Why Separate Database Schemas?
Rationale:
- Bounded Context Isolation: Each service owns its schema (
bc_inventory,bc_ordering) - Independent Deployment: Schema migrations don’t affect other services
- Data Integrity: No cross-schema foreign keys prevent accidental coupling
- Future-Proof: Easy to split into separate databases if needed
Why Hybrid Sync/Async Communication?
Why Hybrid Sync/Async Communication?
Synchronous REST is used when:
- User expects immediate feedback (seat availability check)
- Operation is simple and fast (query events)
- Strong consistency is required within a single context
- Operation involves multiple services (checkout → payment → fulfillment)
- Process is long-running (payment gateway integration)
- Services should remain decoupled (notification doesn’t block fulfillment)
Related Concepts
Microservices Design
Learn about service boundaries, independence, and deployment strategies
Event-Driven Architecture
Deep dive into Kafka events, async workflows, and choreography
CQRS Pattern
Understand command/query separation with MediatR
Hexagonal Architecture
Detailed exploration of ports, adapters, and dependency inversion
