Architecture - Rades98/AlzaCaseStudy GitHub Wiki

Clean architecture

Clean architecture is a category of software design pattern for software architecture that follows the concepts of clean code and implements SOLID principles.

  • Independent of UI Using clean architecture, we should be able to change UI or presentation layers easily without changing the application layer and so on. UI can be from any front-end framework, or console UI, any web, and can be replaced without changing the other layers or rest of the system.
  • Database Independent The architecture should be flexible enough to swap the database without affecting the application use cases and entities. The solution can switch the dataset to MS SQL, MySQL, Oracle, MongoDB, or something else.
  • Independent of External agency/libraries/Drivers The business rules should be independent of external parties or agencies.
  • Framework Independent The core business or application rules should be independent of the existence of frameworks, libraries for the future. We can include the frameworks but as tools, and the solution should not be completely relied on those.
  • Testable The architecture should comply with the testing of the core application and business cases and rules without the UI, database, Web server, or any external component.

Layers

  • Core
    • Application layer
      1. Cache
      2. CQRS
    • Domain layer
  • Infrastructure
    • Persistence layer
      1. EntityFramework
      2. Dapper
  • Presentation layer
    • API
      1. Endpoints
      2. Middleware
      3. Extensions

Core

Is the centre of design.

Domain layer

No dependencies, no project or class reference, no logic. Domain are only objects (entities), which we want to store in same storage (f.e. sql server) Here is no logic applied, these are only recipes how should stored data look like.

Application layer

Only Domain is added as reference project, Pure business logic or services. Application layer tells us how should communication between core and the rest of application should look like. So here we have some interfaces whose implementation makes application functionalitz logic. But yet this layer doesn't know how those work. There are only interfaces, whose tell that here are some data.

Than there are services whose takes those provided data and with some implemented logic sends them to presentation layer, which is in this case API.

Infra structure

Used for data storage and it can be data used in application as records or logs from application.

Persistence layer

This is a storage of application data. Here we have database witch its feed and migrations. Database context must implement interface from application layer.

Presentation layer

This is a presentation web UI project. This can be an MVC, front-end framework. If we are designing an API based solution, then we can keep both Web API and Front-end in this folder host. We add Application and Infrastructure as reference in this project.

This is a way of organizing and designing a clean architecture solution. This is one way of structuring the solution following the clean architecture principles. However, we can do the organization in several ways, keeping the core values intact.

API

This is where the all magic is joined to several endpoints, which are (at least in this case) RESTFul.

Mediator

The mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program's running behavior.

In object-oriented programming, programs often consist of many classes. Business logic and computation are distributed among these classes. However, as more classes are added to a program, especially during maintenance and/or refactoring, the problem of communication between these classes may become more complex. This makes the program harder to read and maintain. Furthermore, it can become difficult to change the program, since any change may affect code in several other classes.

With the mediator pattern, communication between objects is encapsulated within a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces the dependencies between communicating objects, thereby reducing coupling.

CQRS

Command-query separation (CQS) is a principle of imperative computer programming. It was devised by Bertrand Meyer as part of his pioneering work on the Eiffel programming language.

It states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both. In other words, asking a question should not change the answer. More formally, methods should return a value only if they are referentially transparent and hence possess no side effects.

RESTFul

A RESTful API is an architectural style for an application program interface API that uses HTTP requests to access and use data. That data can be used to GET, PUT, POST and DELETE data types, which refers to the reading, updating, creating and deleting of operations concerning resources.

An API for a website is code that allows two software programs to communicate with each other. The API spells out the proper way for a developer to write a program requesting services from an operating system or other application.

A RESTful API -- also referred to as a RESTful web service or REST API -- is based on representational state transfer REST, which is an architectural style and approach to communications often used in web services development.

REST technology is generally preferred over other similar technologies. This tends to be the case because REST uses less bandwidth, making it more suitable for efficient internet usage.

HATEOAS

Hypermedia as the Engine of Application State (HATEOAS) is a constraint of the REST application architecture that distinguishes it from other network application architectures.

With HATEOAS, a client interacts with a network application whose application servers provide information dynamically through hypermedia. A REST client needs little to no prior knowledge about how to interact with an application or server beyond a generic understanding of hypermedia.

By contrast, clients and servers in Common Object Request Broker Architecture (CORBA) interact through a fixed shared through documentation or an interface description language (IDL).

The restrictions imposed by HATEOAS decouple client and server. This enables server functionality to evolve independ

My implementation

In this project I am using RadesSoft.HAteoasMaker which I have created to handle all the stuff. There is attribute called HateoasResponse and is used as you can see in code bellow

[HateoasResponse("user_login", nameof(LoginUserAsync), 1)]
public async Task<ActionResult<UserLoginResponse>> LoginUserAsync(User user, CancellationToken cancellationToken = default)

Where first argument is name of method for hateoas response, second one is name of caller method to generate all the stuff generically and the last one is version number. By using this attribute is endpoint registered to collection which is then used to create response in controller like:

   result.Links = HateoasMaker.GetByNames(
      new Dictionary<string, string?>
      {
         { nameof(OrdersController.GetOrdersAsync), "onMyOrdersClick" },
	 { nameof(OrdersController.GetOrdersFilteredAsync), "onCartClick" },
	 { nameof(ProductDetailsController.GetProductDetailsPaginatedAsync), "loadProductCards" },
	 { nameof(ProductDetailsController.GetProductDetailByIdAsync), "onProductClick" },
      }, Url.ActionContext.HttpContext.GetRequestedApiVersion()?.MajorVersion ?? 1);

Here we have to make dictionary where keys are endpoints to add and values are optional methed names to links. Result object must contain value of type List, or there can be used some other approach, more like hack like.. This is how it is supposed to be used.

There are extension methods too, to fill data to placeholders of links. Method is called ReplaceInLink(...params...) and can be used with up to 5 replace values (new, old), or there is another approach with Dictionary<string, string> to replace more than 5 values at once.