DDD es una aproximación al desarrollo de software que coloca el dominio de negocio en el centro de todas las decisiones técnicas. El modelo de dominio expresa las reglas y procesos del negocio en código, con un lenguaje ubicuo compartido entre desarrolladores y expertos de dominio.
El dominio completo de este portfolio
Tres bounded contexts completamente separados, cada uno con sus entidades, Value Objects y puertos:
src/Domain/
│
├── Portfolio/ ← Bounded Context 1
│ │
│ ├── Portfolio.php ← Aggregate Root
│ │ Agrupa: PersonalInfo, ContactInfo,
│ │ Skill[], SocialNetwork[]
│ │
│ ├── PersonalInfo.php ← Value Object (readonly)
│ │ Campos: name, title, tagline, bio, location
│ │ Invariante: ningún campo obligatorio vacío
│ │ → InvalidArgumentException en constructor
│ │
│ ├── ContactInfo.php ← Value Object (readonly)
│ │ Campos: email, phone, github, linkedin
│ │ Invariante: email con formato válido
│ │
│ └── PortfolioRepository.php ← Puerto
│ Contrato: find(): Portfolio
│
├── Article/ ← Bounded Context 2
│ │
│ ├── Article.php ← Aggregate Root
│ │ Campos: id, title, slug, excerpt,
│ │ content, publishedAt, updatedAt
│ │
│ ├── Tag.php ← Value Object
│ │ name normalizado a minúsculas en constructor
│ │
│ └── ArticleRepository.php ← Puerto
│ findAll(): Article[]
│ findBySlug(string $slug): ?Article
│
└── ExpertiseArea/ ← Bounded Context 3
│
├── ExpertiseArea.php ← Aggregate Root
│ Campos: id, label, fullTitle,
│ iconType, iconValue, category, description
│ Factory: ExpertiseArea::fromArray(array): self
│
├── ExpertiseCategory.php ← Value Object (backed enum)
│ arquitectura | testing | backend | tooling
│
├── IconType.php ← Value Object (backed enum)
│ monogram | simpleicons | url
│
├── ExpertiseAreas.php ← Colección tipada
│ count(): int
│ toArray(): array ← compatible con Twig
│
└── ExpertiseAreaRepository.php ← Puerto
Contrato: findAll(): ExpertiseAreas
Lenguaje ubicuo en la práctica
El código usa los mismos términos que usaría alguien describiendo un portfolio:
PersonalInfo(noUserDataniProfileRecord)Skillconlevel(noCompetencyEntityconscore)ArticleconpublishedAt(noPostconcreatedAt)ExpertiseAreaconcategory(noKeywordniTag)
Conceptos aplicados
- Value Objects inmutables:
PersonalInfo,ExpertiseCategoryeIconTypevalidan sus invariantes en el constructor. Si el id está vacío →InvalidArgumentExceptionantes de que el objeto exista. - Aggregate Root:
ExpertiseAreaes la puerta de entrada a sus Value Objects.ExpertiseAreasagrupa los 16 ExpertiseArea sin exponerlos individualmente. - Repository interfaces en Domain:
PortfolioRepository,ArticleRepositoryyExpertiseAreaRepositoryviven ensrc/Domain/, nunca enInfrastructure/. - Sin dependencias de framework: ningún archivo de
src/Domain/tieneuse Symfony\...niuse Doctrine\....
Puedes explorar la estructura completa en el repositorio público en GitHub