골랑과 함께 잠수 청결 건물

13309 단어 architecturedesigngo
본문은 Makuake Advent Calendar 2020의 다음날 문장이다.
이 글의 내용은 대Golangでクリーンアーキテクチャに入門する의 번역과 정정이다.

소개하다.


저는 보통 CMS를 개인적으로 개발하고 my blog 자격으로 운영합니다.
이것은 Rubel 라는 단일 프로그램으로 Laravel과 React를 기반으로 구축되었다.
Rubel은 MVC나 Flux 같은 흔치 않은 구조를 선택했다.
익숙한 구조와 편리한 틀을 사용해서 저에게 즐거운 개발 체험을 주었습니다.
그러나 내가 그것을 사용한 몇 년 동안, 나는 빠르게 변화하는 구조와 많은 라이브러리에 대한 의존도 때문에 유지보수의 어려움을 점점 느꼈다.
채택한 틀과 언어가 좋지 않기 때문이 아니라 디자인 자체에 문제가 있다고 생각한다.
그리고 좋은 아이디어를 찾았을 때, 나는 청결한 건물을 발견했다.
현재, 나는 Gobel라는 응용 프로그램을 개발하고 있는데, 이것은 깨끗한 체계 구조를 사용하여 Rubel을 대체하고 있다.
나는 청결 건축에 관한 요강과 내가 실제로 사용한 인상을 한 편 쓰고 싶다.

무엇이 청결한 건축물입니까?


시스템 구조의 역사


청결 건축의 이념이 나타나기 전에 건축 이념이 있었다.
전임
육각형 아키텍처(포트 및 어댑터)
양파 구조
비명 건물
DCI
BCE
기다리다
이런 생각들의 공통된 목적은 이익 분리이다.
  • 프레임에 독립적
  • 테스트 가능
  • 사용자 인터페이스 독립적
  • 데이터베이스에 독립적
  • 기타 기술에 독립적
  • 모든 사물에 대한 의존을 깨고 테스트 가능성을 추구하는 것이 구조의 핵심 이념인 것 같다.

    청결 건축


    이것은 청결한 건축의 설명도이다.
    blog.cleancoder.com의 그림을 참고하여 간소화하였다.

    깨끗한 아키텍처는 하나 이상의 계층으로 구분된 소프트웨어로 구성됩니다.
    각 층마다 자신의 직책이 있기 때문에 우리의 목표는 이익 분리를 실현하는 것이다.
    내부는 외부와 상관없이 외부에서 정의한 내용을 직접 호출하지 않습니다.
    인터페이스를 사용하여 외부에서 내부로의 의존 관계 방향을 분석한다.
    구체적으로 DIP라는 규칙을 사용합니다.

    솔리드


    실체는 가장 중요한 업무 규칙을 봉인했다.
    가장 핵심적인 업무 논리이기 때문에 외부 세계의 영향을 받을 수 없는 부분이다.
    예를 들어 방법, 일련의 데이터 구조와 함수 등을 가진 대상.

    용례


    응용 프로그램에 특정한 업무 규칙을 예로 정의합니다.
    다시 말하면 그것은 체계적인 행위의 논리를 가지고 있다.

    인터페이스 어댑터


    인터페이스 어댑터는 실체와 용례에서 사용하는 데이터 구조를 프레임워크와 드라이버가 쉽게 사용할 수 있는 형식으로 바꾸는 데 사용되는 층입니다.
    이 층에는 컨트롤러, 게이트웨이, 시연자 등의 개념이 포함되어 있다.

    프레임 및 구동 요소


    프레임과 드라이버는 가장 바깥쪽이며 외부 세계에 가장 민감하다.
    외부의 영향이 내부에 영향을 주지 않도록 포장과 인터페이스를 제공한다.
    장치, 웹, UI, DB 및 외부 인터페이스 등의 개념을 포함합니다.

    청결 체계 구조 실현


    Go에서 Clean Architecture의 예제 코드를 준비했습니다.
    go-clean-architecute-web-application-boilerplate
    디렉토리 구조는 다음과 같습니다.
    ./app/
    ├── database
    │   ├── migrations
    │   │   └── schema.sql
    │   └── seeds
    │       └── faker.sql
    ├── domain
    │   ├── post.go
    │   └── user.go
    ├── infrastructure
    │   ├── env.go
    │   ├── logger.go
    │   ├── router.go
    │   └── sqlhandler.go
    ├── interfaces
    │   ├── post_controller.go
    │   ├── post_repository.go
    │   ├── sqlhandler.go
    │   ├── user_controller.go
    │   └── user_repository.go
    ├── log
    │   ├── access.log
    │   └── error.log
    ├── main.go
    └── usecases
        ├── logger.go
        ├── post_interactor.go
        ├── post_repository.go
        ├── user_interactor.go
        └── user_repository.go
    
    디렉토리 구조와 각 레이어의 정의는 다음과 같습니다.
    레이어
    번호부
    프레임 및 구동 요소
    인프라 시설
    이음매
    이음매
    용례
    용례
    솔리드
    영역

    찍다


    DIP(의존반전원칙)는 청정 체계 구조 실천을 둘러싸고 이해해야 할 개념이다.
    DIP는 견고한 원칙으로 모듈 간의 제약에 관한 규칙이며 추상은 세부 사항에 의존해서는 안 된다.
    Clean Architecture는 이 규칙을 사용하여 외부에서 내부로의 종속 방향을 유지합니다.
    인터페이스를 사용하면 기울기를 보호하거나 레이어 간의 제한을 보호할 수 있습니다.
    만약 당신이 각 층의 내부에서 규칙을 성실하게 집행하려면, 각 층의 외부에서 규칙을 집행해야 한다.
    이런 상황에서 인터페이스를 정의하고 추상에 의존하여 의존의 방향을 반전시킨다.
    Golang에서 DIP를 연습하려면 Golang의'인터페이스를 받아들이고 구조로 돌아가는'사상을 이해하는 것이 가장 좋다.
    package examples
    
    // Logger is an interface which will be used for an argument of a function.
    type Logger interface {
        Printf(string, ...interface{})
    }
    
    // FooController is a struct which will be returned by function.
    type FooController struct {
        Logger Logger
    }
    
    // NewFooController is a function for an example, "Accept interfaces, return structs".
    // Also, this style of a function take on a role of constructor for struct.
    func NewFooController(logger Logger) *FooController {
        return &FooController{
            Logger: logger,
        }
    }
    
    이것은 기본적인 실현 모델이라고 생각하는데, 너는 자주 Golang에서 본다.
    이러한 방식으로 인터페이스에 의존하면 변경에 저항하는 코드를 작성하고 쉽게 작성할 수 있는 테스트를 작성할 수 있습니다.
    다음은 Golang에서 DIP 구현의 예입니다.
    먼저 코드는 DIP가 아닙니다.
    package examples
    
    // sqlHandler is a struct for handling sql.
    type sqlHandler struct{}
    
    // Execute is a function for executing sql.
    func (sqlHandler *sqlHandler) Execute() {
        // do something...
    }
    
    // FooRepository is a struct depending on details.
    type FooRepository struct {
        sqlHandler sqlHandler
    }
    
    // Find is a method depending on details.
    func (ur *FooRepository) Find() {
        // do something
        ur.sqlHandler.Execute()
    }
    
    다음은 DIP 코드입니다.
    package examples
    
    // SQLHandler is an interface for handling sql.
    type SQLHandler interface {
        Execute()
    }
    
    // sqlHandler is a struct which will be returned by function.
    type sqlHandler struct{}
    
    // NewSQLHandler is a function for an example of DIP.
    // This function depend on abstruction(interface).
    // This pattern is an idiom of constructor in golang.
    // You can do DI(Dependency Injection) by using nested struct.
    func NewSQLHandler() SQLHandler {
        // do something ...
    
        // sqlHandler struct implments SQLHandler interface.
        return &sqlHandler{}
    }
    
    // Execute is a function for executing sql.
    // A sqlHanlder struct implments a SQLHandler interface by defining Execute().
    func (s *sqlHandler) Execute() {
        // do something...
    }
    
    // FooRepository is a struct depending on an interface.
    type FooRepository struct {
        SQLHandler SQLHandler
    }
    
    // Find is a method of FooRepository depending on an interface.
    func (ur *FooRepository) Find() {
        // do something
        ur.SQLHandler.Execute()
    }
    
    '수용 인터페이스, 복귀 구조'의 사상에 따라 인터페이스를 정의하고 복귀 구조를 통해 의존 관계는 인터페이스로 정향된다.
    이전
    SQLHandler
      ↑
    FooRepository
    
    그 다음
    SQLHandler
       ↓   
    SQLHandler Interface
       ↑
    FooRepository
    
    Clean 구조에서 내부가 외부의 규칙을 이용하려고 할 때 DIP를 적용하고 의존 반전을 실행합니다.

    코드를 읽는 방법


    깨끗한 체계 구조 코드를 읽을 때, 당신은 층 간의 의존 관계를 이해해야 합니다.
    내가 보기에, 만약 네가 밖에서 안으로 코드를 읽는다면, 나는 더욱 이해하기 쉽다고 생각한다.
    go-clean-architecute-web-application-boilerplate의 예에서는 다음과 같습니다.
    매인.가다

    라우터가다・・・인프라 시설
     ↓
    사용자 컨트롤러.가다・・・이음매
     ↓
    사용자 상호 작용.가다・・・용례
     ↓
    사용자 저장소.가다・・・용례
     ↓
    사용자가다・・・영역

    인상.


    많은 서류


    코드가 여러 층으로 나누어져 있기 때문에 파일의 수가 불가피하게 크다.
    그것 자체가 좋거나 나쁜 판단축이 아닐 수도 있지만, 나는 각 층의 파일 수량의 편차가 층 구분에 대한 심사를 야기할 수 있다고 생각한다.
    만약 당신이 실용적인 프로그래머라면, 나는 코드 생성기가 좋은 생각이라고 생각한다.

    공약의 정의


    나는 각 층의 직책과 코드 기준을 명확하게 정의하는 것이 가장 좋다고 생각한다.
    깨끗한 체계 구조는 하나의 설계 전략일 뿐 하나의 틀이 아니기 때문에 나는 틀을 만드는 것과 유사한 방법이 필요하다고 생각한다.
    책임의 정의가 명확하지 않으면 당황할 수도 있다.
    검증과 사무 논리는 어느 층에 정의해야 합니까?
    나는 인코딩 기준이 어느 정도 틀에서 설정된 것이라고 생각하기 때문에 너무 많이 생각하지 않을 수도 있지만 청결한 구조의 정책을 따르기 위해 인코딩 기준을 확고히 준비하는 것이 가장 좋다고 생각한다.

    건축사의 필요성


    팀 개발에서 나는 디자인 전략에 대한 토론이 자주 있을 것이라고 생각한다.
    깨끗한 건축에만 국한된 것은 아니지만 건축가들이 디자인 정책의 최종 결정에 대해 책임을 지는 것이 중요하다고 강력히 생각한다.

    거석이야, 웨이트야?


    나는 이것이 어느 장소에서 우세하느냐에 달려 있다고 생각한다.
    나는 깨끗한 건물이 거석이 좋은지 미세한 서비스가 좋은지 상관없다고 생각하지 않는다.
    나는 양자 간의 관계 중 하나가 쉽게 이동하는 것이라고 생각한다. 그러나 이것은 구조자의 기능, 깨끗한 체계 구조가 이 방면에서 우위를 가지느냐에 달려 있다.

    도구책

  • github - manuelkiessling/go-cleanarchitecture
  • github - rymccue/golang-standard-lib-rest-api
  • github - hirotakan/go-cleanarchitecture-sample
  • Recruit Technologies - Go言語とDependency Injection
  • Clean ArchitectureでAPI Serverを構築してみる
  • github - ponzu-cms/ponzu
  • クリーンアーキテクチャの書籍を読んだのでAPIサーバを実装してみた
  • Go × Clean Architectureのサンプル実装
  • Uncle Bob – Payroll Case Study (A full implementation)
  • 좋은 웹페이지 즐겨찾기