7.2 KiB
Entity Inheritance Hierarchy Decomposition
Overview
This refactoring decomposes the deep entity inheritance hierarchy into a more flexible trait-based architecture. This provides better code reusability, composition, and maintainability.
Architecture Diagram
Before (Deep Inheritance):
AbstractDBElement (ID logic)
└─ AbstractNamedDBElement (name + timestamps)
└─ AttachmentContainingDBElement (attachments)
└─ AbstractStructuralDBElement (tree/hierarchy + parameters)
├─ AbstractPartsContainingDBElement
│ ├─ Category
│ ├─ Footprint
│ ├─ StorageLocation
│ └─ AbstractCompany (company fields)
│ ├─ Manufacturer
│ └─ Supplier
After (Trait Composition):
Traits: Interfaces:
- DBElementTrait - DBElementInterface
- NamedElementTrait - NamedElementInterface
- TimestampTrait - TimeStampableInterface
- AttachmentsTrait - HasAttachmentsInterface
- MasterAttachmentTrait - HasMasterAttachmentInterface
- StructuralElementTrait - StructuralElementInterface
- ParametersTrait - HasParametersInterface
- CompanyTrait - CompanyInterface
Class Hierarchy (now uses traits):
AbstractDBElement (uses DBElementTrait, implements DBElementInterface)
└─ AbstractNamedDBElement (uses NamedElementTrait + TimestampTrait)
└─ AttachmentContainingDBElement (uses AttachmentsTrait + MasterAttachmentTrait)
└─ AbstractStructuralDBElement (uses StructuralElementTrait + ParametersTrait)
├─ AbstractPartsContainingDBElement
│ ├─ Category (gets all traits via inheritance)
│ ├─ Footprint (gets all traits via inheritance)
│ └─ AbstractCompany (uses CompanyTrait)
│ ├─ Manufacturer
│ └─ Supplier
Changes Made
New Traits Created
-
DBElementTrait (
src/Entity/Base/DBElementTrait.php)- Provides basic database element functionality with an ID
- Includes
getID()method and clone helper - Extracted from
AbstractDBElement
-
NamedElementTrait (
src/Entity/Base/NamedElementTrait.php)- Provides named element functionality (name property and methods)
- Includes
getName(),setName(), and__toString()methods - Extracted from
AbstractNamedDBElement
-
AttachmentsTrait (
src/Entity/Base/AttachmentsTrait.php)- Provides attachments collection functionality
- Includes methods for adding, removing, and getting attachments
- Includes clone helper for deep cloning attachments
- Extracted from
AttachmentContainingDBElement
-
StructuralElementTrait (
src/Entity/Base/StructuralElementTrait.php)- Provides tree/hierarchy functionality for structural elements
- Includes parent/child relationships, path calculations, level tracking
- Includes methods like
isRoot(),isChildOf(),getFullPath(), etc. - Extracted from
AbstractStructuralDBElement
-
CompanyTrait (
src/Entity/Base/CompanyTrait.php)- Provides company-specific fields (address, phone, email, website, etc.)
- Includes getters and setters for all company fields
- Extracted from
AbstractCompany
New Interfaces Created
-
DBElementInterface (
src/Entity/Contracts/DBElementInterface.php)- Interface for entities with a database ID
- Defines
getID()method
-
StructuralElementInterface (
src/Entity/Contracts/StructuralElementInterface.php)- Interface for structural/hierarchical elements
- Defines methods for tree navigation and hierarchy
-
CompanyInterface (
src/Entity/Contracts/CompanyInterface.php)- Interface for company entities
- Defines basic company information accessors
-
HasParametersInterface (
src/Entity/Contracts/HasParametersInterface.php)- Interface for entities that have parameters
- Defines
getParameters()method
Refactored Classes
-
AbstractDBElement
- Now uses
DBElementTrait - Implements
DBElementInterface - Simplified to just use the trait instead of duplicating code
- Now uses
-
AbstractNamedDBElement
- Now uses
NamedElementTraitin addition to existingTimestampTrait - Cleaner implementation with trait composition
- Now uses
-
AttachmentContainingDBElement
- Now uses
AttachmentsTraitandMasterAttachmentTrait - Simplified constructor and clone methods
- Now uses
-
AbstractStructuralDBElement
- Now uses
StructuralElementTraitandParametersTrait - Implements
StructuralElementInterfaceandHasParametersInterface - Much cleaner with most functionality extracted to trait
- Now uses
-
AbstractCompany
- Now uses
CompanyTrait - Implements
CompanyInterface - Significantly simplified from ~260 lines to ~20 lines
- Now uses
Benefits
1. Better Code Reusability
- Traits can be reused in different contexts without requiring inheritance
- Easier to mix and match functionality
2. Improved Maintainability
- Each trait focuses on a single concern (SRP - Single Responsibility Principle)
- Easier to locate and modify specific functionality
- Reduced code duplication
3. More Flexible Architecture
- Entities can now compose functionality as needed
- Not locked into a rigid inheritance hierarchy
- Easier to add new functionality without modifying base classes
4. Better Testability
- Traits can be tested independently
- Easier to mock specific functionality
5. Clearer Contracts
- Interfaces make dependencies explicit
- Better IDE support and type hinting
Migration Path
This refactoring is backward compatible - all existing entities continue to work as before. The changes are internal to the base classes and do not affect the public API.
For New Entities
New entities can now:
- Use traits directly without deep inheritance
- Mix and match functionality as needed
- Implement only the interfaces they need
Example:
class MyCustomEntity extends AbstractDBElement implements NamedElementInterface
{
use NamedElementTrait;
// Custom functionality
}
Technical Details
Trait Usage Pattern
All traits follow this pattern:
- Declare properties with appropriate Doctrine/validation annotations
- Provide initialization methods (e.g.,
initializeAttachments()) - Provide business logic methods
- Provide clone helpers for deep cloning when needed
Interface Contracts
All interfaces define the minimal contract required for that functionality:
- DBElementInterface: requires
getID() - NamedElementInterface: requires
getName() - StructuralElementInterface: requires hierarchy methods
- CompanyInterface: requires company info accessors
- HasParametersInterface: requires
getParameters()
Future Improvements
Potential future enhancements:
- Extract more functionality from remaining abstract classes
- Create more granular traits for specific features
- Add trait-specific unit tests
- Consider creating trait-based mixins for common entity patterns