[ASP.NET Core] ASP.Net Core Service 패턴
카테고리: ASP .NET Core
태그: ASP.NET Core C# Service Web
Service 패턴 이란?
서비스 패턴이란 애플리케이션의 비즈니스 로직을 담는 계층을 별도로 두어, UI 계층과 도메인 또는 데이터접근 계층을 분리하기 위한 설계 방식입니다.
보통 Layered Architecture에서 자주 적용되며, 다음과 같은 계층을 가집니다.
Controller에서 입력을 받고, 로직을 처리할 때, Service에 캡슐화된 비지니스 로직을 호출하여 사용하는 방식을 말합니다. 그리고 Service는 추상화된 Repository에 요청하여 DB의 정보를 가져와서 처리하게 됩니다.
하지만, Asp.net Core 에서는 DBContext로 Repository 계층을 이미 잘 추상화 해놓았기 때문에, Repository 패턴을 굳이 구현하지 않아도 된다는 목소리가 큽니다. Repository 패턴을 구현하면 과한 추상화로 구조가 오히려 복잡히질 수 있기 때문입니다. 그래서 Service 계층까지만 사용하는걸 선호합니다.
Microsoft의 공식문서 TodoApi
만들기를 보면, 위의 계층없이 Controller에서 모든 행동을 처리하는것을 볼 수 있습니다. 간단한 Api를 만들떄는 해당 방식이 좋을 수 있으나, 로직이 복잡해지고, 재사용할 비지니스 로직이 많아질수록 Controller에 코드가 집중되기때문에 유지보수 및 수정에서 단점이 발생합니다.
따라서 Service 패턴을 구현하여 결합도와 관심사를 낮추는 방식으로 해결합니다.
Service 패턴 구현하기
User데이터를 조작하는 예제를 가져왔습니다. User는 다음과 같이 정의합니다.
namespace AspDemo.Models.User
{
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
}
}
그리고 해당 User를 조작하기 위해 Dto를 만듭니다. 이 예제에서는 유저 1명 조회
, 전체 유저 조회
, 새로운 유저 삽입
이렇게 3가지 기능을 만듭니다. 그에 맞게 Dto를 만들어 줍니다.
namespace AspDemo.Models.User
{
public class UserInfoDto
{
protected UserInfoDto() { }
public long Id { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
}
}
namespace AspDemo.Models.User
{
public class CreateNewUserDto
{
public string? Name { get; set; }
public int Age { get; set; }
}
}
그다음으로는 Service 계층을 만듭니다. Service 계층을 만들 때, interface를 정의하여 사용하고, Controller에서 Interface를 DI 받도록 합니다.
using AspDemo.Models.User;
public interface IUserService
{
public Task<UserInfoDto?> GetUserByIdAsync(long _id);
public Task<List<UserInfoDto>?> GetAllUsersAsync();
public Task InsertNewUser(CreateNewUserDto _user);
}
그 다음에는 해당 인터페이스를 구현한 구현 클래스 UserService
를 만듭니다.
using AspDemo.Data;
using AspDemo.Models.User;
using AutoMapper;
using Microsoft.EntityFrameworkCore;
public class UserService : IUserService
{
public MySqlContext context;
public IMapper mapper;
public UserService(MySqlContext _context, IMapper _mapper)
{
context = _context;
mapper = _mapper;
}
public async Task<UserInfoDto?> GetUserByIdAsync(long _id)
{
var user = await context.User.FindAsync(_id);
if (user == null)
{
return null;
}
return mapper.Map<UserInfoDto>(user);
}
public async Task<List<UserInfoDto>?> GetAllUsersAsync()
{
var userEntityList = await context.User.ToListAsync();//ToListAsync 는 요소가 없을 때 리스트를 만들고 Count = 0 을 반환
return mapper.Map<List<UserInfoDto>>(userEntityList);
}
public async Task InsertNewUser(CreateNewUserDto _createNewUserDto)
{
var newUser = mapper.Map<User>(_createNewUserDto);
context.User.Add(newUser);
await context.SaveChangesAsync();
}
}
이렇게 하면, Service 계층의 작업이 완료 됩니다. 이제 이 Service 를 DI 받은 Controller를 구현합니다.
using AspDemo.Models.User;
using Microsoft.AspNetCore.Mvc;
namespace UnityApiServer.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private IUserService userService;
private ILogger<UserController> logger;
public UserController(IUserService _userService, ILogger<UserController> _logger)
{
userService = _userService;
logger = _logger;
}
[HttpGet("{id}")]
public async Task<ActionResult<UserInfoDto>> GetUser(long id)
{
var userInfoDto = await userService.GetUserByIdAsync(id);
if (userInfoDto == null)
{
return NotFound();
}
return userInfoDto;
}
[HttpGet("all")]
public async Task<ActionResult<List<UserInfoDto>>> GetAllUsers()
{
var userInfoDtoList = await userService.GetAllUsersAsync();
if (userInfoDtoList == null)
{
return NotFound();
}
return userInfoDtoList;
}
[HttpPut("insert")]
public async Task<ActionResult> InsertNewUser([FromBody] CreateNewUserDto newUser)
{
if (newUser == null)
{
return BadRequest();
}
try
{
await userService.InsertNewUser(newUser);
}
catch (Exception ex)
{
logger.LogError(ex, "InsertNewUser error");
return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while inserting new user.");
}
return Ok();
}
}
}
해당 Service를 받아 구현한 Controller 입니다. Controller의 역할이 분명해지고, 코드 라인수도 줄어들었으며 가독성도 향상되었음을 볼 수 있습니다.
댓글 남기기