Strategic architecture, tactical design, and testable code principles (SOLID, Clean Architecture, Design Patterns, Testable Design)
97
97%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Breaking a monolith into microservices doesn't solve architectural problems. Each service still needs internal architecture. Services are a deployment option, not an architecture.
Incorrect (microservices as architecture replacement):
"We use microservices architecture"
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ order-service │ │ user-service │ │ payment-service │
│ │ │ │ │ │
│ routes.js │ │ routes.js │ │ routes.js │
│ database.js │ │ database.js │ │ database.js │
│ helpers.js │ │ helpers.js │ │ helpers.js │
│ │ │ │ │ │
│ (No layers, │ │ (No layers, │ │ (No layers, │
│ no boundaries,│ │ no boundaries,│ │ no boundaries,│
│ just smaller │ │ just smaller │ │ just smaller │
│ messes) │ │ messes) │ │ messes) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
# Result: Distributed monolith
# All the downsides of microservices (network, deployment, consistency)
# None of the benefits (each service still a tangled mess)Correct (clean architecture within each service):
┌───────────────────────────────────────────────────────────┐
│ order-service │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ domain/ │ │
│ │ Order.ts OrderLine.ts OrderStatus.ts │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ application/ │ │
│ │ PlaceOrderUseCase.ts │ │
│ │ ports/ │ │
│ │ OrderRepository.ts │ │
│ │ PaymentGateway.ts ← calls payment-service │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ infrastructure/ │ │
│ │ PostgresOrderRepository.ts │ │
│ │ HttpPaymentGateway.ts → payment-service API │ │
│ │ KafkaEventPublisher.ts │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ interface/ │ │
│ │ OrderController.ts │ │
│ │ OrderEventHandler.ts │ │
│ └─────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────┘
# Each service has:
# - Domain layer with business rules
# - Application layer with use cases and ports
# - Infrastructure layer implementing ports
# - Clean dependency direction within serviceCross-service communication:
// application/ports/PaymentGateway.ts
interface PaymentGateway {
charge(amount: Money, method: PaymentMethod): Promise<PaymentResult>
}
// infrastructure/HttpPaymentGateway.ts
class HttpPaymentGateway implements PaymentGateway {
async charge(amount: Money, method: PaymentMethod): Promise<PaymentResult> {
// Calls payment-service over HTTP
// Service boundary is an infrastructure detail
const response = await fetch('http://payment-service/charge', { ... })
return this.mapResponse(response)
}
}Benefits:
clean-architecture
evals
references
design-patterns
solid-principles
testable-design