DEV Community

Cheedge Lee
Cheedge Lee

Posted on

A simple but practical demo of Web Api in C#

only show the basic steps, as for the other part, version control, CICD, Unit Test, just let it alone, we will focus on the basic structure of the project and the optimise of our code.

  1. initial project
  2. architecture design CQRS, DDD, ES
  3. EF and DTO mapping
    • DB design, Code First
  4. DIP & IoC
  5. CRUD implement
  6. Extra settings: log, api document

Again, I should say it is just a simple demo for newbies to come familiar with these basic concepts, and it’s just some basics from a junior devs daily work. Therefore it is also suitable for someone who want to find a junior dev’s position. If you want more detailed info about each aspect, and deeper philosophy behind the scene, I really cannot help, I am just the screw of the machine, not the driver...

1 Initial Project

By using Visual Studio to create an ASP.Net project, we can directly using its UI, select WebApi project, here we choose to use full web api instead of mini one. More conveniently, we can use command line dotnet new webapi with name to create the project.

2 Architecture Design

2.1 CQRS

The relative more modern architecture to build the WebApi project is CQRS (Command Query Responsibility Segregation), which you can just analogy it with the Read/Write separation in DB management, where command is the write operations (Create, Update, Delete), while query is stand for the read operation, therefore combining the write (create, update, delete) and the read query(retrieve) we can get all the operations of data.

2.2 DDD

DDD stands for Domain Driven Design, in more human-readable words, making the object readable to real world business.

DDD is a separated layer, which should deal with the basic business logic, instead of handling the entity, DTOs mapping.

On architecture aspect, we will see later, when we talk the relationship between about EF entity and DTO class. DDD domain model is an extra layer between EF entity and DTO class.

Entity <--> Domain layer <--> DTO

Repository/Reader/Writer -> Entity and Domain

Handlers -> Domain and DTOs

2.3 ES

Normally we keep a table to record the final results of the entity, for example, for each account we will have a current balance, this is the most common way we see, the table have only the final status of the entity. And this is a good way for read data, where we can directly get the current state. While on the other hand, a single final state which means squash all the previous states, when we try to roll back, there will be problems [1], to fix this, here comes Event-Sourcing, ES is a approach to record each operations so that the states can be build depend on each steps rather than the only final states.

[1] Notice: here we only want to explain the necessary of ES, for roll back or recovery, there are many other approaches without using ES. For example, for a traditional financial system, we always have the extra journal/ledger/audit tables in backoffice systems, which makes the system more robust, and secure. There are many materials, so we don’t expand it here, also I will have another note to discuss about it.

Here in order to further reduce the coupling, we will separate the event and the aggregate, therefore, we will have a separated EventContext and the other Entity DbContext.

2.4 Architecture choice

Feature specific

DDD type

3 EF and DTO

The basic functionality for a web api is to make the decoupling of the frontend and backend, and provide some standard entry for frontend to use to CRUD the data. And this decoupling approach we will see everywhere, and in some extent, all SOLID principles are related with decoupling more or less, in my view. Later we will see the DIP, which is the significant loose coupling approach. Here by using EF entity and the DTO class, we could realise that not mixing up the properties which the DB owns with the properties that can be directly used in CRUD, so that we will not expose all the properties, only provide the necessary entry.

3.1 Entity Framework

For EF entity, we could using the well established Entity Framework (choose the compatible version with your .net version) for this data access process. Here we met with another choices: Code First Design vs DB First Design.

Code First Design vs DB First Design

Here I don’t want to explain too much on these two concepts, if you like, there are many documents, blogs, tutorials discuss the difference and details about the two methods. We just list some common way to choose:

  1. DB already exist, or you prefer to write SQL use DB first.
  2. for new project, Code First is a good choice.

we can choose the Code First approach, and we could either use

  • Attribute Annoation
  • Fluent Api (edit in OnModelCreating method)

    for the table creating. And then using the dotnet ef migration command for the initial, add, update, remove. It’s very convenient for users to manage the database.

3.2 DTO

After automatically generating the entity, as we discuss above, it’s better to use another layer for apis to use the entity, which is the DTO. We can do all the operations on the DTO, and the DTO should include only the necessary properties which we want to operate on. Moreover, we also need to build the bridge between entity and the DTO class, and we could also build a separated Mapper class by using the automapper or you can crated your own automapper via reflection/expression tree, (if you are interesting in this, pls check my another blog [here]).

Read/Write DTO

One step further, if we use the traditional CRUD methods, we could also separate the DTO into Read/Write DTOs, which means when we do the Get method, we can use the Read DTO, where only stores the properties Get method needs; while when we use Create/Update/Delete methods, we will change the data, some of the immutable properties should not be include, therefore we will choose the Write DTO.

3.3 Domain Model

As we discussed in DDD section, a concrete example for DDD implementation is the Domain Model layer between EF Entity and the DTO. Rather than including simple properties, Domain Model are more linking with business entity, aggregate, logic, further more, it also has the behaviours.

For example, the

Instead of directly building the mapper between entity and DTO, we break it into two steps:

EF Entity <--> Domain Model

Domain Model <--> DTO

For each steps build the mapper and then we can still use the DTO for all operations.

If take it more rigorous, we should say there is another mapping,

DbContext <--> EF Entity

which we already discussed in above section Code First Design vs DB First Design.

Notice: here we only separate the Entity which will be exposed through a public API or send them over the wire, which means the events entity should be internal, as we don’t want those events to expose as a public API. These internal-only events (for projections, logging, integration handlers) don’t need DTOs, so they work directly with our IDomainEvent types and the EventEntity persistence model.

4 DIP & IoC (DI)

4.1 DIP

As we discussing in above sections, in order to loose coupling so that making code more flexible for changing and extension. Additionally, it also easy for Unit Test. Still, there are many books, tutorials, blogs to explain the necessary, advantage, and the details of DIP, so we will not discuss it deeply here ([here] is also an unfinished tutorial for some basic info). What I want to simply show is the two basic approaches for implementations in web api.

4.1.1 Architecture Level

On architecture level decoupling, we will extract the corresponding interface of each layer, for example, for the Business Logic Layer(BLL) we can extract a separated interface layer for it (as IBLL), so that we can reduce the coupling.

4.1.2 Component (class) Level

On component level decoupling, instead of directly using the instance of the class, we will use the interface during the declaration.

4.2 IoC (DI)

And we will also discuss another related concept IoC and DI, here DI stands for dependency injection. More accurately, DI is one concrete form of IoC. As the name suggests, IoC is to give the control to the other — the invoke side.

4.2.1 Architecture Level

On Architecture level, IoC mainly reflects on the usage of container. When we initial the web api, we could found the built-in container in the main entry (Program.cs). Individually, we can also use other containers, most common containers as: Castle.Windsor, Unity, Autofac.

4.2.2 Component (class) Level

On component (class) level, we could see the introducing of interface in method parameters. Instead of directly using the class as the parameter, we will use the interface during the parameters passing. By this, we will give the control to the invoking side.

5 CRUD

Till now all the entity, model, dto has been created, then we can build the reader/writer/handlers

5.1 Pure Reader/Writer

Here by "pure" I mean these readers/writers are purely responsible only for communicating with database, so the operations here are clear CRUD ops. And the logic for readers/writers should be complete and simple for basic ops with DB.

Readers Methods

  • GetEntityById

  • GetEntitiesByField

  • GetEntitiesByMultiFields

  • GetAll

Writers Methods

  • AddNewEntity

  • UpdateEntity

  • DeleteEntity

  • SaveChanges

By this, we can not only make our Unit Tests easier, but also separate the Business Logic and the Data Access more clearly, which means we will put all business logics into the Query/Command Handlers and only need to do unit test on the handlers.

5.2 Repository ( DDD+ES )

By using DDD with ES, we normally not directly deal with the CRUD operations, instead we will accumulate the events to get the final status. Therefore we will put the read and write operations into one repository, and there are two basic ops:

  • SaveNewEvent

    • ProjectEvent/AppendEvent
  • GetCurrentState

SaveNewEvent to the event table, at the same time update the existing or new created object table (eg. AccountTbl). And GetCurrentSate of current object table, which is the get operation.

Therefore, now under this pattern the workflow looks like:

  1. Controller get/process request

  2. Handler assemble the event from passed in request

  3. Repository use Event Domain Model to reconstruct the command and update states.

Top comments (0)