• Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Web API Architecture: Services and Repositories

Lomanu4 Оффлайн

Lomanu4

Команда форума
Администратор
Регистрация
1 Мар 2015
Сообщения
1,481
Баллы
155
In the

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

, we added global error handling and logging using NLog. Now that we’ve made our API more resilient, it’s time to go forwards with cleaning up the architecture and separate concerns.

In this post, we’ll cover:

  • Introduce a clean project structure
  • Create Service and Repository layers
  • Register dependencies with Dependency Injection
  • Hook everything up in a simple API endpoint

This sets the foundation for building a maintainable and testable API going forward.

Why Clean Architecture?


A clean arhitecture helps us keeping business logic separate from infrastructure layer and make our project easier to test and extend and, in the end, it improves readability and maintanability over time.

Proposed Project Structure


We’ll split the solution into multiple class libraries to keep things modular and clean:


  • Sample.Api contains API-specific setup like controllers and middleware.


  • Sample.Services includes business logic, independent of controllers or data sources.


  • Sample.Repositories handles communication with the database.


  • Sample.Entities contains models that map directly to your database schema.


  • Sample.DTO defines what’s exposed via API—keeping entities encapsulated.

Each layer references only what it needs. For example:


  • Sample.Services references Sample.Repositories, Sample.DTO, Sample.Entities


  • Sample.Repositories references Sample.Entities


  • Sample.Api references Sample.Services, Sample.Repositories and Sample.DTO
Lets Create Migration With Some Test Data


Update your Migrations with:


[Migration(20250503001)]
public class Mig20250503001_UsersTestData : Migration
{
public override void Down()
{
throw new NotImplementedException();
}

public override void Up()
{
Insert.IntoTable("Users").Row(new
{
Username = "john.doe",
Email = "john.doe@email.com"
});

}
}
}
Repository


In the repository project we need to add SqlKata library. More info about SqlKata can be found in one of my

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.




using Sample.Entities;
using SqlKata.Execution;

namespace Sample.Repositories
{
public interface IUserRepository
{
int InsertNewUser(User user);
User GetUser(int userId);
User GetUser(string username);
}
public class UserRepository : IUserRepository
{
private readonly QueryFactory _db;
private const string TableName = "Users";

public UserRepository(QueryFactory db)
{
_db = db;
}

public int InsertNewUser(User user)
{
return _db.Query(TableName).InsertGetId<int>(user);
}

public User GetUser(int userId)
{
return _db.Query(TableName).Where(nameof(User.Id), userId).Get<User>().Single();
}

public User GetUser(string username)
{
return _db.Query(TableName).Where(nameof(User.Username), username).Get<User>().Single();

}
}
}
Service


namespace Sample.Services
{
public interface IUserService
{
int InsertNewUser(UserDTO user);
UserDTO GetUser(string username);
}
public class UserService : IUserService
{
private readonly IUserRepository _repository;
public UserService(IUserRepository repository)
{
_repository = repository;
}

public UserDTO GetUser(string username)
{
var entity = _repository.GetUser(username);
return new UserDTO()
{
Username = entity.Username,
Email = entity.Email
};
}

public int InsertNewUser(UserDTO user)
{
var entity = new User()
{
Username = user.Username,
Email = user.Email
};
return _repository.InsertNewUser(entity); }
}
}

Here is just a simple implementation given, but the base idea is for service classes to manage interaction between Repositories and Api Controllers and do all the things in between about mapping data and additional business logic.

Register all in Program.cs



....

builder.Services.AddScoped(provider =>
{
var connection = new SqlConnection(connectionString);

var compiler = new SqlServerCompiler();

return new QueryFactory(connection, compiler);
});

builder.Services.AddScoped<IUserRepository, UserRepository>();

builder.Services.AddScoped<IUserService, UserService>();

....
Controller


namespace Sample.Api.Controllers
{
[Route("[controller]")]
[ApiController]
public class UserController : ControllerBase
{

private readonly IUserService _service;
public UserController(IUserService service)
{
_service = service;
}

public UserDTO GetUserByUsername(string username)
{
return _service.GetUser(username);
}
}
}
Summary


With the new project structure in place:


  • Business logic lives in the service layer


  • Data access is cleanly separated in a repository


  • Models are split into entities and DTOs


  • The API is minimal and focused on routing and HTTP interaction

This is a scalable foundation that makes future growth and testing straightforward.

Coming Up Next…


In Part 4, we’ll focus more on testing and API versioning.

Stay tuned!


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх Снизу