Go로 Clean Architecture API 쓰기

16091 단어 GoCleanArchitecture

입문


나는 Go로 청결 구조 아이디어를 채택한 코드를 써 보았다.
이미 많은 사람들이 소스를 썼으니 하나의 예로 볼 수 있기를 바란다.
소스(API, 추가 정보 및 레이블 새로 등록 및 보기 가능)
https://github.com/muroon/memo_sample

청결 체계 구조의 설정도와 출처 목록



왼쪽은 청결 체계 구조의 구조도다.
오른쪽에 모든 원본이 청결 체계 구조에 속하는 층을 표시합니다.

Clean Architecture란 무엇입니까?


Clean Architecture(청결 구조)는 Robert C. Martin(Uncle Bob)이 제시한 구조입니다.
이 책 읽는 사람 많죠.
Clean Architecture가 사람들에게 배우는 소프트웨어 구조와 디자인

배포 이점

  • 쓰기 쉬운 테스트
    간편한 Mock 생성 및 테스트 구현
  • 프레임에 의존하지 않음
    기술 환경은 쉽게 변화하고 진화한다
    그렇게 지도 모른다, 아마, 아마...
    동일도메인 (도메인 제어 설계에서 말하는 도메인)을 사용하여 웹 및 콘솔 아키텍처 작성
  • 데이터 리소스와 독립적
    프레임워크에 의존하지 않는 것과 마찬가지로 기술 환경은 변화에 대응하기 쉽다
    예를 들어, postgreSQL에서 MySQL 또는 Mongo, KVS 등
  • 으로 변경

    규칙


    네 층


    응용 프로그램의 각 기능을 다음과 같은 네 개의 층으로 나눈다.
    계층화
    컨텐트
    Enterprise Business Rules
    솔리드 모델
    Application Business Rules
    담당 업무 논리
    Interface Adapter
    Controller, Presenter
    Frameworks & Drivers
    Frwamework, Database, View
    Enterprise Business Rules
    업무 규칙의 데이터 구조를 가진 대상.
    데이터의 실제 상황을 나타내는 장소.
    Application Business Rules
    업무 규칙을 사용하는 위치.
    즉, 이 프로그램을 실천하면 무엇을 할 수 있는지.
    Interface Adapter
    외부 입력, 데이터 지속화 및 표시 위치 담당
    Frameworks & Drivers
    네트워크 프레임워크, 데이터베이스 작업의 실제 출처,
    프런트엔드의 UI 등이 여기에 속합니다.

    외부 요소를 직접 참조하지 마십시오.



    위의 그림에서 이 화살표는 의존을 나타낸다
    내부층과 외부층 원소의 의존 관계를 금지한다.
    여기서 말하는 의존은 원소(구조, 변수 등)를 직접 인용하지 않는 것을 가리킨다.
    그러면 어쩔 수 없이 바깥쪽의 층 요소를 참조해야 하는데 어떡하지?
    예를 들어 업무 논리에서 데이터 접근 대상(설치 후 자원으로 관리)
    이런 상황에서 반드시 인터페이스를 사용해야 한다.
    자세한 내용은 참조 의존성 역전 원칙

    근원에 관하여


    새로 등록, 필기 및 태그 열람이 가능한 RestAPI 응용 프로그램입니다.
  • 관련 태그를 첨부할 수 있는 추가 정보
  • 태그 및 메모는 N 대 N
  • Repository


    인터페이스


  • 응용 프로그램의 사용 요구에 따라 라벨과 노트에 각각repository 설정
  • tag_repository
  • memo_repository
  • 트랜잭션 담당_repository 설정
  • 설치부


    자료 파일 라이브러리의 실현은adapter에서 설정한 것이다.

    이번에는 다음 두 가지를 준비했습니다.
  • db
  • memory(소스만 해당, Mock 사용)
  • 메모리 라이브러리에 대해db와memory가 분리된 것은
    예를 들어 usecase의 단일 테스트를 할 때db가 아닌 Mock을 잠시 사용할 수 있습니다.
    규모가 커지면 각각db와memory를 준비하는 데 비용이 더 들 수 있습니다
    어떤 자료고만 모크로 만들고 싶은 경우도 있을 것 같습니다.

    사무가 없다

    // memo save
    _, err = memoRepo.Save(ctx, "Memo Text")
    if err != nil {
        panic(err)
    }
    
    // tag save
    _, err = tagRepo.Save(ctx, "Tag Text")
    if err != nil {
        panic(err)
    }
    
    

    하면, 만약, 만약...

    defer func() {
        if err := recover(); err != nil {
            transactionRepo.Rollback(ctx)
        }
    }()
    
    // begin
    ctx, err := transactionRepo.Begin(ctx)
    if err != nil {
        panic(err)
    }
    
    // memo save
    _, err = memoRepo.Save(ctx, "Memo Text")
    if err != nil {
        panic(err)
    }
    
    // tag save
    _, err = tagRepo.Save(ctx, "Tag Text")
    if err != nil {
        panic(err)
    }
    
    // commit
    _, err = transactionRepo.Commit(ctx)
    if err != nil {
        panic(err)
    }
    
    즉, memo 메모리 라이브러리와 tag 메모리 라이브러리의 방법은 업무가 있든 없든 같은 방법을 사용할 수 있다.
    방법 내부에 사무가 있습니까?
    여기에서도goroutine를 지원하고 병행 처리도 실행할 수 있습니다.
    https://github.com/muroon/memo_sample/blob/master/adapter/db/multi_thread_test.go

    Controller, Presenter


    인터페이스



    presenter의 인터페이스는 UseCase 레이어에 속합니다.

    설치부



    컨트롤러와 presenter의 실제 처리는 인터페이스 어댑터 층에 기록되어 있습니다.

    Controller


    input용 모델에 request 매개 변수를 삽입하여 UseCase Interactor에 처리를 전달합니다.
    func (c Controller) PostMemo(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()
    
        ctx = addResponseWriter(ctx, w)
    
        ipt := &input.PostMemo{Text: r.URL.Query().Get("text")}
        c.it.PostMemo(ctx, *ipt) // ←Interactorに処理を渡している
    }
    

    UseCase Interactor


    Interactor에서 처리하여 Presenter에 전달합니다.
    func (i Interactor) PostMemo(ctx context.Context, ipt input.PostMemo) {
    
        // 新規メモの登録
        id, err := i.memo.Post(ctx, ipt)
        if err != nil {
            i.pre.ViewError(ctx, err) // ←Presenterに処理を渡している
            return
        }
    
        // 登録メモの取得
        iptf := &input.GetMemo{ID: id}
        memo, err := i.memo.GetMemo(ctx, *iptf)
        if err != nil {
            i.pre.ViewError(ctx, err) // ←Presenterに処理を渡している
            return
        }
    
        i.pre.ViewMemo(ctx, memo) // ←Presenterに処理を渡している
    }
    

    Presenter


    프레젠테이션에서 뷰 모델을 생성하고 표시합니다.
    func (m presenter) ViewMemo(ctx context.Context, md *model.Memo) {
        defer deleteResponseWriter(ctx)
        w := getResponseWriter(ctx)
    
        m.JSON(ctx, w, m.render.ConvertMemoJSON(md))
    }
    

    마지막


    저는 Dependency Injection을 이용하여 환경 변화에 대응하는 구조를 만들자는 아이디어가 대단하다고 생각합니다.
    나는 내가 하는 데 이렇게 오랜 시간이 걸릴 줄 알았다.
    그러나 밥 아저씨도 정확한 소프트웨어를 만드는 것은 결국 가장 빠른 소프트웨어 제작을 초래할 것이라고 말했다.

    참고 자료


    [밥 아저씨의 Clean Architecture 요약] 대상을 향한 ~ SOLID의 원칙~
    청결 체계 구조를 실시하다
    Clean Architecture를 사용하여 API Server 구축 시도
    당신들의 자료 파일은 과연 틀렸다
    청결 구조의 책을 읽었기 때문에 API 서버를 설치해 보았다

    좋은 웹페이지 즐겨찾기