Clean Architecture
with ASP.NET Core
STEVE SMITH
ARDALIS.COM | @ARDALIS |
[email protected]DEVIQ.COM
Learn More After Today
1) Pluralsight
◦ N-Tier Apps with C# http://bit.ly/PS-NTier1
◦ Domain-Driven Design Fundamentals http://bit.ly/ddd-fundamentals
2) DevIQ
◦ ASP.NET Core Quick Start http://aspnetcorequickstart.com
3) Microsoft FREE eBook/Sample App
◦ eShopOnWeb eCommerce Sample https://ardalis.com/architecture-ebook
4) Contact me for mentoring/training for your company/team
Questions
HOPEFULLY YOU’LL KNOW THE ANSWERS WHEN WE’RE DONE
Why do we separate applications into multiple
projects?
What are some principles we can apply when
organizing our software modules?
How does the organization of our application’s
solution impact coupling?
What problems result from certain common
approaches?
How does Clean Architecture address these
problems?
Oh yeah, and isn’t ASP.NET Core pretty cool?
:)
Principles
A BIT OF GUIDANCE
Separation of Concerns
Avoid mixing different code responsibilities in the
same (method | class | project)
The Big Three™
Data Access
Business Rules and Domain Model
User Interface
Single Responsibility
Works in tandem with Separation of Concerns
Classes should focus on a single responsibility – a
single reason to change.
Following Don’t Repeat Yourself…
Refactor repetitive code into functions
Group functions into cohesive classes
Group classes into folders and namespaces by
Responsibility
Level of abstraction
Etc.
Further group class folders into projects
Invert (and inject) Dependencies
Both high level classes and implementation-detail classes should
depend on abstractions (interfaces).
Classes should follow Explicit Dependencies Principle:
Request all dependencies via their constructor.
Corollary
Abstractions/interfaces must be defined somewhere accessible by:
Low level implementation services
High level business services
User interface entry points
Make the right thing easy
and the wrong thing hard.
UI classes shouldn’t depend directly on infrastructure classes
◦ How can we structure our solution to help enforce this?
Business/domain classes shouldn’t depend directly on infrastructure classes
◦ How can our solution design help?
Repetition of (query logic, validation logic, policies, error handling, anything) is a
problem
◦ What patterns can we apply to make avoiding repetition easier than
copy/pasting?
“Classic” N-Tier
Architecture
OR N-LAYER
Source: MSDN Website, 2001
N-Tier Benefits
Team
Code Reuse
Segmentation
(slightly)
Better
Looser
Maintainability
Coupling
N-Tier Drawbacks
Transitive (some)
Dependencies Complexity
(still)
Tight
Coupling
Transitive Dependencies
User Interface
Layer
Business Logic
Layer
Everything
Depends on the database
Data Access
Layer
DB
Domain-Centric Design
AND THE CLEAN ARCHITECTURE
Domain Model
Not just “business logic”
A model of the problem space composed of Entities, Interfaces, Services, and more.
Interfaces define contracts for working with domain objects
Everything in the application (including infrastructure and data access) depends on these
interfaces and domain objects
Clean Architecture
Onion Architecture
Hexagonal Architecture
Ports and Adapters
Clean Architecture “Rules”
The Application Core contains the Domain Model
All projects depend on the Core project; dependencies point inward toward this core
Inner projects define interfaces; outer projects implement them
Avoid direct dependency on Infrastructure project (except from Integration tests)
Clean Architecture Features
Framework Independent.
◦ You can use this architecture with ASP.NET (Core), Java, Python, etc. It doesn’t rely on any software
library or proprietary codebase.
Database Independent
◦ The vast majority of the code has no knowledge of what database, if any, might be used by the
application. Often, this knowledge will exist in a single class, in a single project that no other project
references.
UI Independent
◦ Only the UI project cares about the UI. The rest of the system is UI-agnostic.
Testable
◦ Apps built using this approach, and especially the core domain model and its business rules, are
extremely testable.
Refactoring to a Clean Architecture
Best to start from a properly organized solution
◦ See http://github.com/ardalis/CleanArchitecture
Next-best: Start from a single project
Most difficult: Large, existing investment in multi-layer architecture without abstractions or DI
The Core Project
Minimal dependencies – none on Infrastructure.
What Goes in Core:
Interfaces Domain Events
Event Handlers
Entities Value Objects Aggregates
Domain
Services
Specifications
Exceptions
The Infrastructure Project
All dependencies on out-of-process resources.
What Goes in Infrastructure:
EF (Core) Cached
Repositories System Clock
DbContext Repositories
Web API
Clients
File System Logging
Accessors Adapters
Email/SMS Other
Sending Interfaces
Services
The Web Project
All dependencies on out-of-process resources.
What Goes in Web:
Razor
Controllers Views Or
Pages
ViewModels ApiModels BindingModels
Tag/Html
Filters Binders
Helpers
Other Services Interfaces
Sharing Between Solutions:
Shared Kernel
Common Types May Be Shared Between Solutions. Will be referenced by Core project(s).
Ideally distributed as Nuget Packages.
What Goes in Shared Kernel:
Base Domain Base
Base Entity
Event Specification
Common Common
Exceptions Interfaces
Common Auth Common Common
Common DI
e.g. User class Logging Guard Clauses
Guard Clauses?
Simple checks for input that use common rules and exceptions.
Nuget Package: Ardalis.GuardClauses (https://github.com/ardalis/GuardClauses)
Example:
public void ProcessOrder(Order order)
{
Guard.Against.Null(order, nameof(order));
// process order here
}
Solution Structure – Clean Architecture
Functional Integration
Tests Web Infrastructure Tests
Unit Tests Core
Shared Kernel
Typical (Basic) Folder Structure
What belongs in actions/handlers?
Controller Actions or Page Handlers should:
1) Accept task-specific types (ViewModel, ApiModel, BindingModel)
2) Perform and handle model validation (ideally w/filters)
3) “Do Work” (More on this in a moment)
4) Create any model type required for response (ViewModel, ApiModel, etc.)
5) Return an appropriate Result type (View, Page, Ok, NotFound, etc.)
“Do Work” – Option One
Repositories and Entities
1) Get entity from an injected Repository
2) Work with the entity and its methods.
3) Update the entity’s state using the Repository
Great for simple operations
Great for CRUD work
Requires mapping between web models and domain model within controller
.
“Do Work” – Option Two
Work with an application service.
1) Pass ApiModel types to service
2) Service internally works with repositories and domain model types.
3) Service returns a web model type
Better for more complex operations
Application Service is responsible for mapping between web models and domain model.
Keeps controllers lightweight, and with fewer injected dependencies.
“Do Work” – Option Three
Work with commands and a tool like Mediatr.
1) Use ApiModel types that represent commands (e.g. RegisterUser)
2) Send model-bound instance of command to handler using _mediator.Send()
No need to inject separate services to different controllers – Mediatr becomes only dependency.
Code Walkthrough
Resources
Online Courses (Pluralsight and DevIQ)
• SOLID Principles of OO Design http://bit.ly/SOLID-OOP
• N-Tier Architecture in C# http://bit.ly/PS-NTier1 and http://bit.ly/PS-NTier2
• DDD Fundamentals http://bit.ly/ddd-fundamentals
• ASP.NET Core Quick Start http://aspnetcorequickstart.com/ DEVINTFALL17 20% OFF!
• Weekly Dev Tips Podcast http://www.weeklydevtips.com/
• Microsoft Architecture eBook/sample http://aka.ms/WebAppArchitecture