ORM + AKKA 조합은 데이터중심에서 메시지 중심 설계로 가기위한 도전과제이며

.NetCore에 AKKA를 내장하기위해서 .net core 의존성 주입(dependency-injection) 기법을 사용해보겠습니다.


의존성주입

마이크로 서비스당 ActorSystem이 하나가 필요하며, 하나만 필요하기때문에 싱글톤객체를 통해 생성이 가능합니다.

.net core asp.net이 가진 라이프사이클내에서 ActorSystem도 유지될 필요가 있기때문에

.net core에서 제공하는 의존성 제어 방법을 사용해야합니다. 

전통적인 생성자내부에서 의존성 생성 케이스

public class AccountControler : ControllerBase
{
    private readonly AccountContent _context;
    private readonly AccountService _service;

    public AccountControler()
    {
        _context = new AccountContent();
        ActorSystem actorSystem = new ActorSystem("accountapi");
        _service = new AccountService(_context, actorSystem);
    }    
.....
}

전통적으로 우리가 가진 생성자에서, 의존성이 있는 객체를 생성하는 방법입니다.  위 생성자의 문제는 객체의 라이프사이클을

관리하기가 어렵고, 결과적으로 오동작하는 코드가 될 가능성이 높습니다.


객체 의존 요구사항을 정리해봅시다.

  • AccountController은 DB를 제어하는 AccountContent와 별도의 서비스 AccountService를 가질수 있습니다.
  • AccountService는 AccountContent와 ActorSystem을 가질수 있습니다.

위 설계는 아래 요구사항을 만족을 할수가 없습니다.

  • AccountController은 사용자 요청에의해 여러객체가 생성될수 있습니다.
  • AccountContent는 DB트랜잭션당 하나의 객체가 생성될수 있습니다.
  • ActorSystem은 오직 하나의 객체만 존재해야합니다.


위와같은 라이프사이클이 다른 객체의 의존관계를 올바르게 설정하는것은 몇가지 빌드패턴을 혼용해서

해결을 할수 있지만, 객체 설계의 복잡성을 늘리는것은 우리가 원하는 방식이 아닙니다.

외부전략에의한 의존설정을 했을시객체 설계가 단순해지는지 살펴보겠습니다.

태고적에는 의존성 충돌을 유연하게 회피하는 구조 설계를 할수 있는 역량은

숙련개발자와 비숙련개발자를 나누는 요소였습니다. C++에서 include를 사용하여 수많은 오브젝트들을

포함시키고 문제없이 호출하고 해제해야하는 개발방법에 대한 책만해도 수십종에 이릅니다.

이러한 구조적 설계문제를 추상화하고 단순화해주는 템플릿을 활용하는 디자인 패턴이 발전함으로

여러언어 제네릭프로그래밍에 영향을 준것도 사실입니다.


오늘날에는 이러한 구조적인 설계가 대부분 프레임워크또는 툴킷에 녹여져 있으며 우리에게 구속복을 입힙니다.

어떻게 설계를 해서 어플리케이션 레이아웃을 만들것인가? 란 고민이 대부분 어디에 프로그래밍을해야 작동을 하는가? 로 변경되었으며

두가지 고민중 실제 서비스코드를 작성 시작할수 있기까지 어느것이 더 빠른지는 명확합니다.


의존성을 주입하는 방법

    public class AccountControler : ControllerBase
    {
        private readonly AccountContent _context;
        private readonly AccountService _service;
 
        public AccountControler(AccountContent context, 
            ActorSystem actorSystem,
            AccountService accountService)
        {
            _context = context;
            _service = accountService;
        }
////////////////////
///////////////////
    public class AccountService
    {
        private readonly AccountContent _context;
        private readonly ActorSystem _actorSystem;

        public AccountService(AccountContent context, ActorSystem actorSystem)
        {
            _context = context;
            _actorSystem = actorSystem;

            Console.WriteLine(_context.Users.First(p => p.UserId == 1).NickName);
            System.Console.WriteLine("Actor System Check===" + actorSystem.Name);
        }

위와같이 생성자에 어떠한 의존성에대한 생성코드를 작성하지 않습니다. 필요한 객체를 단지 외부에 생성된

객체 참조를 통해서만 ( 인터페이스 구현으로 더 우아하게 전달될수도 있습니다.) 사용하고

AccountController와 AccountService는 서로간의 생성에 관여를 하지 않습니다.


실제 생성전략을 정하는곳

    public class Startup
    {

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices( IServiceCollection services )
        {
            services.AddDbContext<AccountContent>(opt => {
                opt.UseMySql("server=localhost;database=db_account;user=psmon;password=db1234");
            });

            services.AddSingleton<ActorSystem>(_ => ActorSystem.Create("accountapi"));

            services.AddScoped<AccountService>();

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);            
        }
    ..........

객체 생성의 설정과 패턴을 정할수 있는 가장 적합한곳은.. 어플리케이션이 구동된후 설정을 읽을수 있는곳입니다.

객체 생성의 옵션과 전략을 지정한것만으로... 이후 작성한 객체의 생성자에 적합한 의존성이 자동으로 주입이됩니다.

라이프사이클을 고려한 생성전략

  • Transient : 요청을 받을때마다
  • Scoped : 요청 당 한 번 생성
  • Singleton : 처음으로 요청 받을 때에 생성. 이후의 요청들은 최초에 생성된 인스턴스를 사용

Controller은 사용자의 요청에 대응하여 상태저장없이 사용될수 있기때문에 Transient 범주로 보면되고

사용자의 요청마다  AccountService 가 생성될필요가 없음으로 최초에 필요한시점 하나만 생성되는  Scoped 범위로 볼수가 있습니다.

ActorSystem은 System당 하나만 존재하면 되므로 Singleton 전략을 선택할수가 있습니다.



추가 정보



  • No labels