Loan Module
The Loan module demonstrates Bus Mode (CQRS) with business logic, domain rules, and cross-aggregate operations. It manages book borrowing and returns.
Domain Layer
Entity: Loan
The Loan entity references User and Book by ID (not direct object references), maintaining aggregate boundaries:
class Loan extends EntityAbstract
{
public function isOverdue(DateTimeImmutable $now): bool
{
return $this->returnedAt === null && $this->dueAt < $now;
}
public function markAsReturned(DateTimeImmutable $returnedAt): void
{
if (!$this->status->canBeReturned()) {
throw LoanAlreadyReturnedException::forLoan($this->id);
}
$this->status = LoanStatus::Returned;
$this->returnedAt = $returnedAt;
}
}
LoanStatus Enum
Commands and Queries
| Command/Query | Purpose |
|---|---|
BorrowBookCommand | Create a new loan |
ReturnBookCommand | Mark loan as returned |
ListLoansQuery | All loans (admin) |
MyLoansQuery | Current user's loans |
Business Rules
- A book can only be borrowed if its status is
Available - A loan can only be returned if its status is
Active - Overdue detection based on current date vs due date
Domain Events
LoanCreatedEvent- When a book is borrowedLoanReturnedEvent- When a book is returned
Cross-Aggregate Interaction
When borrowing a book, the handler:
- Validates book availability via
BookRepository - Creates a new
Loanentity - Updates book status to
Borrowed - Persists both changes
- Dispatches
LoanCreatedEvent
Permissions
loan.view_all- View all loans (admin only)loan.view_own- View own loans (admin and user)loan.borrow- Borrow and return books
See Also
- Bus Mode - Architectural pattern used
- CQRS - Commands and Queries
- Entities - Loan entity with business rules
- Enums - LoanStatus enum
- Domain Events - LoanCreatedEvent, LoanReturnedEvent
- Commands & Handlers - Cross-aggregate operations