Product Decisions This Supports
- Architectural Evolution: Transition from procedural or anemic domain models to a rich DDD architecture, enabling better scalability for complex business domains (e.g., e-commerce, SaaS platforms, or financial systems).
- Bounded Context Adoption: Structurally enforce bounded contexts to isolate domain logic, reducing merge conflicts and enabling independent team ownership of features (e.g., "Order Processing" vs. "Inventory Management").
- Domain-Driven Prioritization: Shift focus from infrastructure concerns (e.g., database schemas, APIs) to business value, aligning development with stakeholder priorities (e.g., "How does this support our subscription model?").
- Build vs. Buy: Avoid reinventing DDD infrastructure (e.g., repositories, value objects, event handling) while maintaining flexibility to extend or replace components (e.g., swapping
InMemoryRepository for Doctrine later).
- Use Cases:
- Greenfield Projects: Launch with DDD from day one (e.g., a new SaaS platform).
- Legacy Refactoring: Gradually introduce DDD to modularize monolithic Laravel apps (e.g., extracting "Billing" into a bounded context).
- Microservices Readiness: Prepare for decomposition by encapsulating domain logic in reusable modules.
- Regulatory Compliance: Model business rules explicitly (e.g., GDPR data handling, financial auditing) to ensure traceability.
When to Consider This Package
-
Adopt if:
- Your project has complex business rules that require explicit modeling (e.g., workflows, validation logic, or domain-specific calculations).
- Your team is investing in long-term maintainability and willing to upskill in DDD (expect 3–6 months of ramp-up).
- You’re building a modular monolith or microservices and need to decouple domain logic from infrastructure.
- You’re using Laravel and want to avoid framework-specific DDD solutions (e.g., Symfony bundles) while keeping PHP-native flexibility.
- You need reusable abstractions for:
- Value Objects (e.g.,
Email, Money, UUID).
- Repositories (e.g.,
UserRepository with pagination, filtering).
- CQRS-like patterns (Commands/Queries/Handlers) without full CQRS frameworks.
- Domain Events (e.g.,
OrderCreated, PaymentFailed).
- You’re integrating with API Platform, Sylius, or Doctrine later (bridges are available).
-
Avoid if:
- Your project is CRUD-heavy with minimal domain logic (e.g., a blog with posts/comments). Overhead isn’t justified.
- Your team lacks PHP/Laravel experience or DDD familiarity. Consider starting with simpler architectures (e.g., Clean Architecture) first.
- You need real-time event sourcing or advanced CQRS (e.g., event versioning, projections). This package focuses on foundational DDD, not these patterns.
- You’re constrained by performance-critical paths (e.g., high-frequency trading). DDD abstractions add latency.
- You’re using non-PHP stacks (e.g., Node.js, Python) or alternative Laravel DDD tools (e.g.,
spatie/laravel-ddd).
-
Look Elsewhere if:
- You need enterprise-grade DDD tools: Evaluate Axon Framework (PHP), EventSauce, or custom solutions for large-scale systems.
- You’re locked into Symfony and prefer its ecosystem (e.g.,
symfony/ux-dropzone, api-platform).
- You require graphQL-native DDD (e.g.,
rebornix/laravel-ddd or spatie/laravel-query-builder).
- Your domain is data-intensive (e.g., analytics, real-time dashboards). Consider ELT pipelines or data mesh approaches.
How to Pitch It (Stakeholders)
For Executives (Business/Strategy)
*"This package helps us build software that adapts to business needs—not the other way around. Right now, our codebase might look like a ‘digital photocopy’ of our business processes, but as we grow, that rigidity will slow us down. With Domain-Driven Design (DDD), we’ll:
- Reduce technical debt by organizing code around business capabilities (e.g., ‘Subscriptions,’ ‘Payments’) instead of technical layers.
- Future-proof our platform to handle changes like new regulations, market expansions, or feature requests without rewrites.
- Improve collaboration between devs, product, and ops by speaking the same language (ubiquitous language) about how the business works.
Think of it like upgrading from a spreadsheet to an ERP system—initially more complex, but it pays off when we scale. For a project like [X Initiative], this could save us 6–12 months of rework down the line. The trade-off? A 3–6 month learning curve for the team, but the ROI is clear."
Ask for:
- Budget for team training (DDD workshops, books like Domain-Driven Design by Eric Evans).
- Approval to pilot DDD in one bounded context (e.g., "Orders") before full adoption.
For Engineering Leaders (Architecture/Tech Debt)
*"This package gives us a scalable, Laravel-native foundation for DDD, which is critical for projects like [X] where:
- Complexity is growing (e.g., workflows, multi-tenant logic, compliance rules).
- Team turnover risks losing institutional knowledge of the domain.
- Legacy code is becoming a bottleneck for new features.
Key Benefits:
-
Separation of Concerns:
- Domain Layer: Pure business logic (e.g.,
Order calculates taxes, validates rules).
- Application Layer: Use cases (e.g.,
PlaceOrderCommand).
- Infrastructure Layer: Laravel/Eloquent/Events (e.g.,
OrderRepository).
No more ‘anemic domain models’ where behavior is scattered across services.
-
Reusable Abstractions:
- Value Objects: Strongly typed
Email, Money, UUID (no more string fields with hidden validation).
- Repositories: Standardized interfaces for
find(), save(), search() (e.g., UserRepository instead of User::query()).
- Commands/Queries: Clean separation for write vs. read operations (e.g.,
CreateUserCommand vs. GetUserQuery).
-
Future-Proofing:
- Microservices-ready: Bounded contexts can become independent services later.
- Testability: Domain logic is unit-testable without databases (e.g., test
Order rules with InMemoryRepository).
- Extensible: Swap out
InMemoryRepository for Doctrine or API Platform later.
Trade-offs:
- Initial Complexity: Expect 20–30% more boilerplate for new features (e.g., defining
User as an entity with invariants).
- Team Upskill: Need to learn DDD concepts (e.g., Aggregate Roots, Domain Events). Propose a 2-day workshop to align the team.
Recommendation:
Start with a pilot bounded context (e.g., ‘Orders’) to validate the approach before full adoption. If successful, we can gradually refactor other domains (e.g., ‘Users’, ‘Payments’)."*
For Developers (Implementation)
*"This package lets us write code that’s closer to the business problem, not just database operations. Here’s how it changes our workflow:
Before (Anemic Domain Model):
// User.php (just a data bag)
class User {
public $name;
public $email;
}
// UserService.php (handles all logic)
class UserService {
public function validateEmail(User $user) { ... }
public function calculateAge(User $user) { ... }
}
After (DDD with ddd-foundation):
// User.php (rich domain model)
class User {
private Email $email;
private string $name;
public function __construct(Email $email, string $name) {
$this->email = $email;
$this->name = $name;
}
public function isEmailValid(): bool {
return $this->email->isValid();
}
}
// UserRepository.php (standardized interface)
class UserRepository implements RepositoryInterface {
public function findByEmail(Email $email): ?User { ... }
}
// CreateUserCommand.php (application layer)
readonly class CreateUserCommand implements CommandInterface {
public function __construct(
public string $name,
public string $email,
) {}
}
// CreateUserHandler.php (handles the command)
#[AsCommandHandler]
class CreateUserHandler {
public function __invoke(CreateUserCommand $command) {
$user = new User(
Email::fromString($command->email),
$command->name,