This project represents the
API
or the « back end » of Solen LMS solution. It's built usingASP.NET Core
and following the principles of Clean Architecture by Robert C. Martin (aka Uncle Bob).
What you need to be installed in your machine to run this app :
- The latest NET Core SDK
- MySQL Server
First, you should get the source code from the GitHub repository. You can either clone the repo or just download it as a Zip
file.
# clone the repo
git clone https://github.com/imanys/Solen.Api.git
The configuration file appsettings.json
is not checked into the repository. Instead, there is a template file called appsettings.template.json
located in Src\Main\Solen
directory which you can rename to appsettings.json
.
Click here to see all the settings
{
"AppSettings": {
// The application uses SignalR to send Web Socket notifications. In order to enable Cross-Origin Requests (CORS), SignalR requires to specify the expected origins.
// The default value corresponds to the address used by Solen-SPA App (https://github.com/imanys/Solen-SPA) when it's locally installed.
"CorsOrigins": "http://localhost:4200"
},
"ConnectionStrings": {
"Default": "Server=localhost; Database=Solen-db; Uid=app-user; Pwd=app-user-password"
},
"Serilog": {
"Using": [],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Warning"
}
},
"Enrich": [
"FromLogContext",
"WithMachineName",
"WithProcessId",
"WithThreadId"
],
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "Seq",
"Args": {
"serverUrl": "http://localhost:8081"
}
}
]
},
"Security": {
"Key": "JWT Tokens secret key", // The secret key used to generate JWT Tokens
"JwtTokenExpiryTimeInMinutes": "1", // JWT Token expiry time in minutes. JWT Tokens should be short-lived
"RefreshTokenExpiryTimeInDays": "1", // Refresh Token expiry time in days.
"IsSigninUpEnabled": true // Indicates whether or not the signing up process should be enabled
},
"AllowedHosts": "*",
"SwaggerOptions": {
"Enable": true, // Indicates whether or not swagger should be available
"Url": "/swagger/v1/swagger.json",
"Name": "Solen LMS API"
},
"EmailSettings": {
"ApiKey": "sendGrid API Key", // Solen API uses this field as the SendGrid key to send emails
"From": "no-reply@address.com",
"IsPickupDirectory": true, // When it's set to true, emails are generated in a Pickup directory. Otherwise, the emails are sent by SendGrid.
"PickupDirectory": "C:\\Temp\\SolenMails" // The path to the Pickup directory
},
// Local storage settings. By default, the app uses the local storage to store resources
"LocalStorageSettings": {
"BaseUrl": "http://localhost:5000",
"ResourcesFolder": "Resources",
"ImagesFolder": "Images",
"VideosFolder": "Videos",
"RawFolder": "Raw"
},
"CompleteOrganizationSigningUpPageInfo": {
"Url": "http://localhost:4200/signing-up/organization/complete",
"TokenParameterName": "token"
},
"CompleteUserSigningUpPageInfo": {
"Url": "http://localhost:4200/signing-up/user",
"TokenParameterName": "token"
},
"ResetPasswordPageInfo": {
"Url": "http://localhost:4200/auth/reset",
"TokenParameterName": "token"
}
}
💣 Since the application checks whether the database is up to date with all EF Core migrations
at the startup time, you should define a valid ConnectionStrings
before running the app.
# change directory to the « Main » folder
cd Solen.Api/Src/Main/Solen
# build the solution
dotnet build Solen.Api.sln
# run the solution
dotnet run
The application should now be listening on http://localhost:5000
:
There are three ways to « discover » Solen API
:
A great way to discover and tests APIs
, is using Swagger / OpenAPI
. To access the Swagger UI
via a browser, run Solen API
application, access the application's URL and add /swagger
at the end of it. The swagger UI
should show up :
Swagger
is enabled ("Enable": true
) in the SwaggerOptions
section of the appsettings.json
file.
As the application uses Specflow (a framework to support BDD
for .NET
applications), we can use tools like Pickles to generate a living documentation using the specifications written in Gherkin
. To access this documentation, navigate to https://doc.api.solenlms.com.
Automated Acceptance Tests
, thus the living documentation is not yet complete!
Finally, you can install Solen SPA, the Angular
« front end » application of Solen LMS
, and check out how Solen SPA
makes calls to Solen API
.
As stated at the beginning of this document, the Solen API
architecture is inspired by the Clean Architecture.
The idea of Clean Architecture, is to put the Business Logic and Rules (aka
Policies
) at the centre of the design, and put theInfrastructure
(akamechanisms
) at the edges of this design.
The Business Rules are divided between two layers: the Domain
layer (aka Entities) and the Application
layer (aka Use Cases). The Domain
layer contains the enterprise business rules, and the Application
layer contains the application business rules. The difference being that enterprise rules could be shared with other systems whereas application rules would typically be specific to this system.
These two layers form what is called the Core
of the system.
What makes this architecture work is that all dependencies must flow inwards. The Core
of the system has no dependencies on any outside layers. Infrastructure
, Persistence
... depend on Core
, but not on one another.
Source code dependencies must point only inward, toward high-level policies.
This is the architectural application of the Dependecy Inversion Principle
.
A Clean Architecture produces systems that have the following characteristics :
- Independent of frameworks.
Core
should not be dependent on external frameworks such asEntity Framework
. - Testable. The business rules can be tested without the UI, database, web server, or any other external element.
- Independent of the UI. The UI can change easily. For example, we can swap out the
Web UI
for aConsole UI
, orAngular
forReact
. Logic is contained withinCore
, so changing the UI will not impact the system. - Independent of the database. We can change
SQL Server
forOracle
,Mongo
, or something else.Core
is not bound to the database. - Independent of any external agency.
Core
simply doesn't know anything about the outside world.
For further reading about Clean Architecture, I highly recommend this book.
This layer contains the Entities of Solen API
. There are business objects that contain reusable Business Logic.
There are absolutely NOT simple data structures. This layer is independent of data access concerns and has no dependencies.
This layer encapsulates and implements all the Use Cases of Solen API
. In general, each Use Case is independent of the others (Single Responsibility Principle
).
For example, in the Users Management
« module », modifying or deleting the Use Case InviteMembers
will have absolutely no effects on the BlockUser
Use Case.
To tackle business complexity and keep use cases simple to read and maintain, the Application
layer implements the architectural pattern CQRS. Using this pattern means clear separation between Commands (Write operations) and Queries (Read operations). This separation gives us, we developers, a clear picture of what pieces of code change the state of the application.
To keep the application business rules out the external layers and to prevent this layers from knowing much about the Business Logic,
the Application
layer implements the Mediator Pattern.
To implement the CQRS
Pattern and the Mediator
Pattern easily, the Application
layer makes use of an open source
.NET library called MediatR. It allows in-process messaging and provides an elegant and
powerful approach for writing CQRS
.
When starting using MediatR
, whe should first define a Request
. A Request
can be either a Query
or a Command
.
Once a Request
is created, we need a Handler
to execute the Request
.
When exposing public APIs
, it is important to validate each incoming request to ensure that it meets all expected pre-conditions.
The system should process valid requests but return an error for any invalid requests.
The validation process is part of the application business logic. Therefore, the responsibility for validating requests does not
belong within the Web API
or Console UI
or whatsoever external interfaces, but rather in the Application
layer.
To make the validation process easier, we make use of a popular .NET library for building validation rules called FLUENT VALIDATION.
The other advantage of using this library, is we can make use of MediatR
pipeline behaviours to validate automatically every
request that requires validation before further processing occurs.
The Infrastructure
layer contains by two sub-layers : The Persistence
sub-layer and the Infrastructure
sub-layer.
The Persistence
layer is typically the implementations of all repository interfaces defined in the Application
layer.
The layer is actually implemented based on Entity Framework Core
. All the configurations related to EF Core
are also implemented in this layer.
The Infrastructure
sub-layer implements all interfaces (other than repositories
) from the Application
layer to provide
functionality to access external systems or to perform some technical tasks :
- Sending notifications (Web Sockets with
SignalR
, Emails withSendGrid
) - Accessing resource files (on File System or on the Cloud)
MediatR
pipeline behaviours- Security (Passwords generator, JWT Tokens generator...)
- ...
The Presentation
layer is the entry point to the system from the user’s point of view. Its primary concerns are routing requests to the Application
layer.
In the context of Solen API
, the Presentation
layer contains ASP.NET Core Web APIs
. All the concerns related to the GUIs
are handled by Solen SPA application.
In the "traditional" way to write controllers, we usually implement some business logic flow in like as Validation, Mapping Objects, Return HTTP status code...
Example of a Fat Controller :
[HttpPost]
public async Task<IHttpActionResult> CreateCourse(CourseModel model) {
if (!ModelState.IsValid) return BadRequest (ModelState);
var course = new Course {
Title = model.title
};
var result = await _coursesService.CreateCourse(course);
if (!result.Succeeded) return GetErrorResult (result);
return Ok ();
}
Using the Clean Architecture (where all the business logic and rules are implemented in the Core
layer),
and a library like Mediadtr
, we can write controllers with few lines of code.
With such dumb controllers, we have no need to test them.
The Main
is just the entry point to the application and where all configurations are registered (Dependency Injection, Security...).
NUnit is the framework used for unit-testing all the Core
layer components : Requests Handlers
, Requests Validators
, Services
and Domain Objects
.
Specflow is the framework used to implement Acceptance Tests
.
Acceptance Tests
, yet! The work is in progress 🚧 😎
For the moment, I will be the only contributor of the project. Nevertheless, you're welcome to report bugs or/and submit features by creating issues.