A C# project demonstrating SOLID design principles, Test-Driven Development (TDD), acceptance testing, and clean code practices.
LibraryManagement/
├── LibraryManagement.Domain/ # Core business logic
│ ├── Entities/ # Domain entities
│ ├── Interfaces/ # Abstractions
│ └── Services/ # Domain services
├── LibraryManagement.Infrastructure/ # Implementation details
│ ├── Repositories/ # Data access
│ └── Services/ # External services
├── LibraryManagement.Application/ # Entry point
├── LibraryManagement.Tests/ # Unit tests
└── LibraryManagement.AcceptanceTests/ # Integration tests
# Run the demo application
dotnet run --project LibraryManagement.Application
# Run unit tests
dotnet test LibraryManagement.Tests
# Run acceptance tests
dotnet test LibraryManagement.AcceptanceTests
# Run all tests
dotnet test
Each class has one reason to change:
Book
(LibraryManagement.Domain/Entities/Book.cs:1
) - Manages book state and borrowing rulesMember
(LibraryManagement.Domain/Entities/Member.cs:1
) - Manages member information and borrowing limitsBookBorrowingService
(LibraryManagement.Domain/Services/BookBorrowingService.cs:1
) - Handles borrowing/returning logicOverdueBookService
(LibraryManagement.Domain/Services/OverdueBookService.cs:1
) - Manages overdue book notifications
The system is open for extension but closed for modification:
- Repository Pattern - New storage implementations can be added without changing existing code
- Notification Services - New notification types (SMS, push notifications) can be added via
INotificationService
- Date/Time Providers - Different time sources can be injected for testing or different environments
Derived classes are substitutable for their base classes:
- Repository Implementations -
InMemoryBookRepository
can be replaced with database implementations - Service Implementations -
EmailNotificationService
can be replaced with other notification providers - Date Providers -
SystemDateTimeProvider
can be replaced with mock providers for testing
Clients depend only on interfaces they use:
IBookRepository
(LibraryManagement.Domain/Interfaces/IBookRepository.cs:1
) - Focused on book data operationsIMemberRepository
(LibraryManagement.Domain/Interfaces/IMemberRepository.cs:1
) - Focused on member data operationsINotificationService
(LibraryManagement.Domain/Interfaces/INotificationService.cs:1
) - Focused on notifications onlyIDateTimeProvider
(LibraryManagement.Domain/Interfaces/IDateTimeProvider.cs:1
) - Minimal interface for time operations
High-level modules don't depend on low-level modules; both depend on abstractions:
- Domain Services depend on interfaces, not concrete implementations
- Application layer composes dependencies through constructor injection
- Infrastructure implements the interfaces defined in the domain
The project follows the Red-Green-Refactor cycle:
- Entity Tests (
LibraryManagement.Tests/Domain/Entities/
) - Test business rules and invariants - Service Tests (
LibraryManagement.Tests/Domain/Services/
) - Test business logic with mocked dependencies
- Integration Tests (
LibraryManagement.AcceptanceTests/LibrarySystemAcceptanceTests.cs:1
) - Test complete user scenarios - End-to-End Workflows - Verify the system works as a whole
- Descriptive Names -
BookBorrowingService
,GetOverdueBooksAsync
- Intention-Revealing -
CanBorrowMoreBooks()
,IsOverdue()
- Consistent Terminology - Borrow/Return throughout the codebase
- Small Functions - Each method does one thing well
- Pure Functions - No side effects where possible
- Meaningful Parameters - Clear parameter names and types
- Fail Fast - Validate inputs immediately in constructors
- Meaningful Exceptions -
InvalidOperationException
with descriptive messages - Guard Clauses - Early returns for invalid conditions
- Separation of Concerns - Domain, Infrastructure, and Application layers
- Dependency Direction - Dependencies point inward toward the domain
- Testable Design - All dependencies are abstracted and injectable