Solution Overview
Bookify is organized as a .NET solution with four projects, each representing a layer in the Clean Architecture:Bookify.Domain Project
The Domain layer is the core of the application with zero external dependencies.Directory Structure
Key Folders
Abstractions/
Abstractions/
Contains base classes and interfaces used throughout the domain:
Entity- Base class for all entitiesIDomainEvent- Interface for domain eventsIUnitOfWork- Transaction abstractionResult- Result pattern implementationError- Error representation
Aggregates (Apartments/, Bookings/, Reviews/, Users/)
Aggregates (Apartments/, Bookings/, Reviews/, Users/)
Each aggregate has its own folder containing:
- Root Entity - The aggregate root (e.g.,
Booking.cs) - Enums/ - Enumeration types
- Events/ - Domain events raised by this aggregate
- Interfaces/ - Repository interfaces
- Services/ - Domain services
- ValueObjects/ - Value objects specific to this aggregate
- Errors - Static error definitions
Shared/
Shared/
Naming Conventions
- Entities:
Booking.cs,Apartment.cs,User.cs - Value Objects:
Email.cs,Money.cs,DateRange.cs - Domain Events:
*DomainEvent.cs(e.g.,BookingReservedDomainEvent.cs) - Repositories:
I*Repository.cs(e.g.,IBookingRepository.cs) - Errors:
*Errors.cs(e.g.,BookingErrors.cs) - Services:
*Service.cs(e.g.,PricingService.cs)
Bookify.Application Project
The Application layer contains use cases and orchestrates domain logic.Directory Structure
Key Folders
Abstractions/
Abstractions/
Contains interfaces and pipeline behaviors:
- Authentication/ - Auth service interfaces
- Behaviors/ - MediatR pipeline behaviors
- Caching/ - Cache service interfaces
- Clock/ - Date/time abstraction
- Data/ - Database connection factory
- Email/ - Email service interface
- Messaging/ - CQRS abstractions
Feature Folders (Apartments/, Bookings/, Users/)
Feature Folders (Apartments/, Bookings/, Users/)
Organized by feature/use case. Each use case has its own folder containing:
- Command/Query - The request object
- Handler - Processes the command/query
- Validator - FluentValidation validator (for commands)
- Response DTOs - Data returned to the API
- Event Handlers - Domain event handlers
ReserveBooking/ReserveBookingCommand.csReserveBookingCommandHandler.csReserveBookingCommandValidator.csBookingReservedDomainEventHandler.cs
Exceptions/
Exceptions/
Application-specific exceptions:
ValidationException- Thrown by validation behaviorConcurrencyException- Thrown on concurrency conflicts
Naming Conventions
- Commands:
*Command.cs(e.g.,ReserveBookingCommand.cs) - Command Handlers:
*CommandHandler.cs - Validators:
*CommandValidator.cs - Queries:
*Query.cs(e.g.,GetBookingQuery.cs) - Query Handlers:
*QueryHandler.cs - Response DTOs:
*Response.cs(e.g.,BookingResponse.cs) - Event Handlers:
*DomainEventHandler.cs
Feature Organization
Each feature is self-contained in its own folder:Bookify.Infrastructure Project
The Infrastructure layer provides implementations for external concerns.Directory Structure
Key Folders
Authentication/
Authentication/
JWT and Keycloak authentication:
AuthenticationService- Keycloak integrationJwtService- JWT token generationUserContext- Current user information- Models/ - Keycloak API models
Authorization/
Authorization/
Caching/
Caching/
Redis caching implementation:
CacheService- ImplementsICacheServiceCacheOptions- Configuration
Configurations/
Configurations/
EF Core entity configurations:
- One configuration file per aggregate root
- Defines table mappings, relationships, and conversions
BookingConfiguration.cs configures the bookings table.Data/
Data/
Database-related utilities:
SqlConnectionFactory- Creates Dapper connectionsDateOnlyTypeHandler- Dapper type handler for DateOnly
Migrations/
Migrations/
EF Core migrations:
- Database schema changes over time
- Generated with
dotnet ef migrations add
Repositories/
Repositories/
Repository implementations:
Repository<T>- Generic repository base classApartmentRepository,BookingRepository, etc. - Specific implementations
EF Core Configuration Example
Bookify.Api Project
The API layer is the entry point for HTTP requests.Directory Structure
Key Folders
Controllers/
Controllers/
API endpoints organized by aggregate:
ApartmentsController- Search apartmentsBookingsController- Manage bookingsReviewsController- Add and view reviewsUsersController- User registration and authentication
Contracts/
Contracts/
Request and response DTOs for the API:
- Maps to commands/queries in the Application layer
- Provides a stable API contract
- Can differ from internal command/query structure
Extensions/
Extensions/
Extension methods for application setup:
ApplicationBuilderExtensions- Middleware registrationSeedDataExtensions- Database seeding
Middleware/
Middleware/
Custom middleware:
ExceptionHandlingMiddleware- Global exception handlingRequestContextLoggingMiddleware- Correlation ID logging
Controller Example
- Accept HTTP requests
- Map to commands/queries
- Send via MediatR
- Return HTTP responses
Application Composition
File Naming Patterns
Consistent naming makes the codebase easy to navigate:Domain Layer
- Entities:
Booking.cs,Apartment.cs - Value Objects:
Money.cs,Email.cs - Domain Events:
BookingReservedDomainEvent.cs - Repositories:
IBookingRepository.cs - Errors:
BookingErrors.cs
Application Layer
- Commands:
ReserveBookingCommand.cs - Command Handlers:
ReserveBookingCommandHandler.cs - Validators:
ReserveBookingCommandValidator.cs - Queries:
GetBookingQuery.cs - Query Handlers:
GetBookingQueryHandler.cs - Responses:
BookingResponse.cs - Event Handlers:
BookingReservedDomainEventHandler.cs
Infrastructure Layer
- Repositories:
BookingRepository.cs - Configurations:
BookingConfiguration.cs - Services:
AuthenticationService.cs,CacheService.cs
API Layer
- Controllers:
BookingsController.cs - Requests:
ReserveBookingRequest.cs
Namespace Organization
Namespaces follow the folder structure:Benefits of This Structure
Feature Cohesion
Everything related to a feature is in one folder. Want to modify booking? Look in
Bookings/.Easy Navigation
Consistent naming and folder structure make files easy to find.
Scalability
New features are added as new folders. Existing features don’t become cluttered.
Clear Dependencies
Project structure enforces dependency rules. Can’t accidentally reference Infrastructure from Domain.
Adding a New Feature
To add a new feature (e.g., “Complete Booking”):
No changes needed in Infrastructure (unless you need new database fields).
Best Practices
One feature, one folder. Keep all files related to a use case together.
How to organize DTOs?
How to organize DTOs?
- API Contracts:
Bookify.Api/Contracts/ - Application Responses: Next to the query handler
- Infrastructure Models: Next to the service that uses them
Where do cross-cutting concerns go?
Where do cross-cutting concerns go?
- Application Layer: Behaviors, abstractions
- Infrastructure Layer: Implementations (caching, logging, etc.)
- API Layer: Middleware
Next Steps
Architecture Overview
Return to the architecture overview
Getting Started
Learn how to set up and run Bookify