Entity Creation Standards
tip
This page is current and ready for use.
Here's a comprehensive set of recommendations for entity design.
1. Constructor Approach
- Include only required fields in constructors
- Set default/calculated values like timestamps in the constructor
- Validate inputs directly in the constructor
- Read-only fields should be initialized via constructor
- Example:
public function __construct(
string $name,
string $careOf,
Gender $gender,
DateTimeImmutable $dateOfBirth
) {
$this->name = $name;
$this->careOf = $careOf;
$this->gender = $gender;
$this->dateOfBirth = $dateOfBirth;
$this->joinedAt = new DateTimeImmutable();
}
2. Setter Methods
- Provide setters for both required and optional fields
- Use a fluent interface (return
$this
) - Include validation in setters to prevent invalid states
- Avoid setters for the read-only fields and initialize value via constructor
- Example:
public function setOwnerType(string $ownerType): self
{
$this->validateOwnerType($ownerType);
$this->ownerType = $ownerType;
return $this;
}
3. Type Safety
- Use strong types for properties and method parameters/returns
- Use Symfony's Uid types for identifiers
- Use enums for categorical data
- Consider value objects for complex types
4. Entity Creation Pattern
-
Use constructors for domain integrity
-
Use setters for optional fields
-
Example:
$user = new User(
$dto->name,
$dto->careOf,
$dto->gender,
$dto->dateOfBirth
);
// Optional fields
if ($dto->nickname) {
$user->setNickname($dto->nickname);
}
5. Naming Conventions
- Use
*_at
suffix for timestamps (e.g.,createdAt
,uploadedAt
) - Be consistent with existing patterns in your codebase
- Use domain terminology in your method names
6. Validation
- Add domain validation in constructors and setters
- Use Symfony's Assert annotations for additional validation
- Create custom domain exceptions for validation failures
7. Domain Behavior Methods
- Include methods that express domain operations
- Use meaningful names that reflect business operations
- Return
$this
for method chaining when appropriate - Example:
public function verify(FileVerification $verification): self
{
$this->verification = $verification;
$this->status = FileStatus::from($verification->getStatus()->value);
return $this;
}