포스트

C# Clean Architecture + DI 구조 설계하기 (2) - Service / Repository 구현하기

1편에서는 전체 구조를 잡고

  • Domain / Application / Infrastructure / Presentation
  • 의존성 방향
  • 프로젝트 분리

▶ 근데 아직 아무것도 안 돌아간다

이번 편에서는
실제 비즈니스 로직을 넣어본다


전체 흐름 다시 보기

이번 편에서 만들 구조는 이거다.

1
Controller  Service  Repository  DB

▶ 핵심은

Controller는 요청만 전달 Service는 비즈니스 처리 Repository는 데이터 접근

1. Domain (Entity 그대로 사용)

1편에서 만든 Entity 그대로 사용한다.

1
2
3
4
5
public class User
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

▶ Domain은 건드리지 않는다 (중요)

2. Application - Repository 인터페이스

먼저 Repository 인터페이스부터 정의한다.

1
2
3
4
5
public interface IUserRepository
{
    Task<User?> GetByIdAsync(Guid id);
    Task AddAsync(User user);
}

▶ 여기서는 “무엇을 할 수 있는지”만 정의한다 ▶ “어떻게 하는지”는 절대 안 씀

3. Application - Service (UseCase)

이제 핵심 로직을 만든다.

1
2
3
4
5
public interface IUserService
{
    Task<User?> GetUserAsync(Guid id);
    Task<Guid> CreateUserAsync(string name);
}

구현체는 이렇게 간다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class UserService : IUserService
{
    private readonly IUserRepository _userRepository;

    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public async Task<User?> GetUserAsync(Guid id)
    {
        return await _userRepository.GetByIdAsync(id);
    }

    public async Task<Guid> CreateUserAsync(string name)
    {
        var user = new User
        {
            Id = Guid.NewGuid(),
            Name = name
        };

        await _userRepository.AddAsync(user);

        return user.Id;
    }
}

▶ 핵심 포인트

Repository를 인터페이스로 주입 DB가 뭔지 전혀 모름 오직 비즈니스 로직만 담당

4. DTO 분리 (중요 👍)

실무에서는 Entity를 그대로 쓰지 않는 게 좋다.

1
2
3
4
public class CreateUserRequest
{
    public string Name { get; set; } = string.Empty;
}

▶ 이유

API 모델과 Domain 분리 변경 영향 최소화 유지보수 편함

5. Controller 연결

이제 Presentation 계층에서 연결한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[ApiController]
[Route("api/users")]
public class UserController : ControllerBase
{
    private readonly IUserService _userService;

    public UserController(IUserService userService)
    {
        _userService = userService;
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> Get(Guid id)
    {
        var user = await _userService.GetUserAsync(id);

        if (user == null)
            return NotFound();

        return Ok(user);
    }

    [HttpPost]
    public async Task<IActionResult> Create(CreateUserRequest request)
    {
        var id = await _userService.CreateUserAsync(request.Name);

        return Ok(id);
    }
}

▶ Controller는 진짜 얇다

로직 없음 검증/호출만 담당 여기까지 구조 상태

이제 흐름은 완성됐다.

Controller → Service → Repository(interface)

▶ 근데 아직 문제 하나 남았다

  • Repository “구현체”가 없음
  • DI 연결도 안 됨
  • DB도 없음

▶ 아직 실행은 안 된다

이번 편 핵심 정리 Service는 비즈니스 로직 담당 Repository는 인터페이스만 정의 Controller는 최대한 얇게 DTO로 외부 모델 분리

다음 포스팅에서는

EF Core 적용 Repository 실제 구현 DI 등록 (Program.cs) 실제 실행까지 연결

▶ 드디어 “돌아가는 구조” 완성한다

Clean Architecture는

▶ 구조만 보면 어렵고 ▶ 한번 흐름 잡으면 오히려 더 단순하다

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

© 곰이. 일부 권리 보유