Edmonds Commerce - Symfony Hexagonal Architecture
Overview
Build maintainable, testable Symfony applications using Hexagonal Architecture (Ports & Adapters), Domain-Driven Design, and CQRS patterns.
What We Build
Applications using hexagonal architecture principles. This approach separates domain logic (your business rules) from infrastructure concerns (databases, APIs, frameworks), creating systems that are easier to test, understand, and evolve.
Hexagonal Architecture Explained
Core Concepts
Domain Layer: Contains all business logic and domain rules. Independent of any framework or infrastructure.
Ports: Interfaces defining how the domain interacts with the outside world. Abstractions representing capabilities needed by your business logic.
Adapters: Implementations of ports. Connect your domain to specific technologies (databases, APIs, frameworks).
Infrastructure: External systems like databases, payment gateways, email services that your application interacts with.
Benefits
Testable Domain Logic: Test your business rules in isolation without databases, APIs, or frameworks. Tests run in milliseconds.
Technology Independence: Swap Doctrine for another ORM, migrate from MySQL to PostgreSQL, or change message brokers without touching business logic.
Clear Boundaries: The separation makes it obvious what each part of your application does. Onboarding new developers becomes easier.
Long-Term Maintainability: Business logic remains stable as technologies evolve around it. No rewriting as frameworks age.
Domain-Driven Design: Model your business domain directly in code using DDD patterns.
Architecture Layers
Layer 1: Domain
Contains business logic, independent of frameworks and databases.
Includes:
- Entities and Value Objects
- Aggregates
- Domain Services
- Domain Events
- Repository Interfaces (ports)
- Business Rules and Constraints
Layer 2: Application
Coordinates domain logic and adapters. Handles use cases and workflows.
Includes:
- Use Case Handlers
- DTO (Data Transfer Objects)
- Application Services
- Queries and Commands (CQRS)
- Request/Response Handlers
Layer 3: Infrastructure
Implements ports, handling concrete technology concerns.
Includes:
- Repository Implementations (adapters)
- Database Models and Queries
- API Clients
- Email Service Implementations
- Event Publisher Implementations
- Configuration
Layer 4: Presentation
Web framework and HTTP-specific concerns.
Includes:
- Symfony Controllers
- HTTP Routes
- Request Validation
- JSON Serialization
- HTTP Error Handling
Domain-Driven Design Patterns
Entities
Objects with identity that persist throughout their lifecycle. Have behaviour and state.
Example: User entity with methods like changePassword(), deactivate().
Value Objects
Immutable objects without identity, defined by their attributes. Multiple instances with same attributes are equivalent.
Example: Email, Money, Address are value objects.
Aggregates
Cluster of entities and value objects bound together. Aggregate roots control access to the aggregate.
Example: Order aggregate contains Line Items, OrderStatus, Customer Address.
Domain Services
Business logic that doesn't naturally fit within entities or value objects.
Example: Pricing service calculating discounts across products.
Domain Events
Fact that something happened in your domain that other systems should know about.
Example: UserRegistered, OrderShipped, PaymentProcessed events.
CQRS Pattern (Command Query Responsibility Segregation)
Separate read and write models for clarity and performance.
Commands: Operations that modify state.
Queries: Operations that read state.
Benefits:
- Clear intent (command vs query)
- Different optimisation strategies
- Separate scaling for read and write
- Event sourcing compatibility
Testing Domain Logic
Domain Tests: Test business rules in isolation.
✅ No database needed
✅ No API calls
✅ No framework dependencies
✅ Tests run in milliseconds
✅ 100% code coverage achievable
Port & Adapter Implementation
Ports (Interfaces)
Define what your domain needs.
interface UserRepositoryPort {
saveUser(User): void
findUserById(id): User
}
Adapters (Implementations)
Concrete implementations using specific technologies.
class DoctrineUserRepository implements UserRepositoryPort {
// Doctrine-specific implementation
}
Switching Adapters
Want to change databases? Implement a new adapter:
class ElasticsearchUserRepository implements UserRepositoryPort {
// Elasticsearch-specific implementation
}
Your domain code doesn't change—just swap adapters.
Technology Stack
- Symfony 7 for framework
- Doctrine ORM for persistence
- PHP 8.1+ for type system
- PHPUnit/Pest for testing
- Event Dispatcher for domain events
- Message Queue for asynchronous processing
- Docker for environment consistency
Our Hexagonal Architecture Process
1. Domain Analysis
Understand your business domain deeply.
- Identify core concepts (entities, aggregates)
- Model value objects
- Define business rules
- Identify domain events
2. Architecture Design
Map domain concepts to hexagonal architecture.
- Define domain layer structure
- Identify ports (repository, payment service, etc.)
- Plan application layer use cases
- Design infrastructure adapters
3. Implementation
Build layer-by-layer.
- Develop domain with comprehensive tests
- Implement adapters for infrastructure
- Create use case handlers
- Build Symfony presentation layer
4. Testing Strategy
Comprehensive testing at multiple levels.
- Unit tests for domain logic (should be fast)
- Integration tests for adapters
- Functional tests for use cases
- End-to-end tests for workflows
Target Audiences
Enterprise Teams: Build systems that remain maintainable for decades.
Long-Term Products: Architecture that survives technology evolution.
Complex Domains: Model complicated business logic with DDD patterns.
Performance-Critical Systems: Optimize read and write paths independently with CQRS.
Common Use Cases
E-Commerce Platforms: Order aggregates, pricing rules, inventory management.
Financial Systems: Account management, transaction processing, reconciliation.
SaaS Platforms: Multi-tenant architectures, subscription management, feature flags.
Content Management: Content aggregates, publishing workflows, versioning.
Migration Path
From Legacy Code: Use hexagonal architecture to modernise existing systems incrementally.
From Monoliths: Extract microservices using hexagonal patterns.
From Mixed Architectures: Gradually enforce hexagonal principles.
Related Services
Symfony Development: Full Symfony expertise for your application.
API Platform: Expose your domain through APIs.
Testing: Comprehensive testing strategies for complex systems.
Team Augmentation: Senior developers to guide hexagonal architecture implementation.
Contact
Based in the UK, serving global enterprises. Discuss your domain architecture, complexity, or modernisation strategy.