알바콘 개발일기 - Day 03

코드 굽는 제빵사·2020년 12월 7일
0

개발일기

목록 보기
4/13

출처 : https://threedots.tech/post/basic-cqrs-in-go/

현재 저는 밥아저씨의 클린 아키텍처를 읽고나서 클린 아키텍처 구조로 코드를 작성하고 있습니다.

USECASE에서 CQRS 패턴을 도입 하려는 이유?

  • 최적화 된 데이터 스키마: 읽기 쪽에서는 쿼리에 최적화된 스키마를 사용하는 반면 쓰기 쪽에서는 업데이트에 최적화된 스키마를 사용 할 수 있습니다.
  • 보안 : 올바른 도메인 엔터티만 데이터에서 쓰기를 수행할 수 있는지 쉽게 확인할 수 있습니다.
  • 문제의 분리 : 읽기 및 쓰기 쪽을 구분하면 유지 가능하고 유연한 모델을 생성할 수 있습니다. 대부분의 복잡한 비지니스 논리는 쓰기 모델로 이동합니다. 읽기 모델은 상대적으로 간단할 수 있습니다.
  • 단순한 쿼리 : 읽기 데이터베이스에서 구체화된 뷰를 저장하여 쿼리 할때 애플리케이션은 복잡한 조인을 방지 할 수 있습니다.
  • 시스템이 시간이 지나면서 진화 할것으로 예상되어 여러 버전의 모델을 포함할 수 있습니다.

마이크로소프트 CQRS 설명서를 참고하세요!

본격적인 알바콘 구현

-CQRS패턴을 넣기 위해 command/query 디렉토리를 나눴고, CQ를 포함하는 usecase.go를 만들었습니다.

모델로 분리하고 그 데이터 모델을 Usecase로 통합 합니다.
아직은 아무것도 만들지 않았기에 빈 구조체입니다.

Command의 company를 등록하는 핸들러를 만들도록 하겠습니다. command에서는 domain의 모델을 쓰는게 아니라 write에 해당하는 모델을 따로 만들어서 사용합니다. write모델을 따로 쓰는게 필수적인 것은 아니지만 필드 중에 외부에 노출 해야 할 필요성이 없는 것을 감출수도 있습니다.

write_model.go

package command

type Company struct {
	Name		string
	Address		string
	JobOpening	bool

write전용 모델은 도메인의 모델과 똑같죠?
아직 모델이 단순하기 떼문에 은닉 할 것도 없습니다. 그래서 domain에 있던 모델을 그대로 가져왔습니다.
추후에 데이터 생성 과정에서 정하지 않아도 되는 필드들이 생긴다면 달라 질겁니다.

register_company.go

package command

import (
	"context"
	"github.com/memoregoing/albacorn/company/domain/company"
)

type RegisterCompanyHandler struct {
	repo company.Repository
}

func NewRegisterCompanyHandler (repo company.Repository) RegisterCompanyHandler {
	if repo == nil {
		panic("nil repo")
	}
	return RegisterCompanyHandler{repo: repo}
}

func (h RegisterCompanyHandler) Handle(ctx context.Context, cmd Company) (err error) {
	co, err := company.NewCompany(cmd.Name, cmd.Address)
	if err != nil {
		return err
	}
	if err := h.repo.AddCompany(ctx, co); err != nil {
		return err
	}
	return nil
}
  • RegisterCompanyHandler 구조체는 repo라는 company.Repository라는 인터페이스 변수를 갖고 있는 것이다. 인터페이스 변수이기 때문에 어떤 타입이 올지 런타임에 동적으로 결정되게 된다. 이것으로 레포 변수의 다형성을 구현한다.
  • NewRegisterCompanyHandler 함수는 의존성 주입(DI: Dependency injection)으로 런타임에 매개변수로 repository인터페이스를 받아 handler를 초기화해줍니다.
  • Handle 함수는 write_model의 company 구조체를 매개변수로 받아서 도메인 모델의 Newcompany함수를 통해 company객체를 생성합니다. 생성 된 객체는 인터페이스로 추상화된 repo.AddCompany를 통해 저장되거나 오류를 발생 시킵니다.

여기서 중요한 점은 Handle함수는 행동이 결정 되었을 뿐 아직 구현되지 않은 상태입니다. 실질적인 구현은 추후 다른 계층에서 이뤄질 것이기 때문입니다. Usecase는 행동을 정의 할 뿐 구현하지 않기 때문에 구현 부분이 변경 되더라도 행동이 변하지 않는다면 Usecase는 변하지 않습니다.

0개의 댓글