도메인 중심의 모범 사례 - 코드를 사용하는 방법 도메인 기반 디자인 당신에게

권한은 사용자의 인증 및 권한 부여를 정복 세입자의 시스템에서 서비스를 수행하기 위해, 우리는 서비스가 명명go-easy-login

     이 논문의 본질에서 실제 권한 시스템 microService의 도메인 중심의 디자인 학습 추가 검토 및 개선 도메인 중심의 디자인 자체는 점진적인 과정은 문화의 개념입니다 객체 지향 미래, 대부분의 사람들을 포함하여 과거와 현재의 프로그래밍 아이디어 필드를, 다만 가죽 옷을 입고 객체 지향 프로세스를하고, 세부 사항을 참조 거친 라이브 데이터베이스에 직면 우리는 도메인 기반의 디자인이 필요한 이유 하지만, 당신은 현장 중심의 설계와 접촉하는 경우,하지만 어떻게 참여하는 아무 생각 고통,하지만 내가 개념을 이해하는 방법을 모른다 당신은 도메인 기반을 가진 특별한 매력을 느끼고,이 문서는 최고의 엔트리 도메인 중심의 디자인 중 하나입니다, 당신은 도메인 중심의 디자인 연습을 위해 도메인 중심의 디자인을 이해하지 않은 경우이 문을 열 것, 여기에서 연습 할 수 있습니다.

프로젝트 구조

     코드 먼저, 코드 및 해당 문서의 디렉토리 구조는, 우리가 처음 YY 읽을 수있는 질문을 한 다음 역할에 해당하고, 수있는 방법을 표시합니다.

login
      base
                encrypt.go
          token.go
          repoImpl.go
      domain
         service
              loginService.go
              loginService_test.go
            loginUser.go
            loginUser_test.go
      mocks
            EncryptHelper.go
            LoginUserRepo.go

당신은 기술적 인 세부 사항을두고 어떻게

     비즈니스 로직 및 해당 필드 (모델)에 도메인 기반 디자인 더 강조는 안 설립
기술적 인 세부 사항, 즉 데이터베이스 캐싱을. 객체 지향 및 시뮬레이션 외부 목적의 것들에 대한 반영
창 사용자 클래스가 있어야한다 eat, drink, play, happy및 기타 기능, 사용자가 데이터베이스에 연결할 수있는 능력을 가질 수 없습니다, 객체 지향 프로그래밍의 기술적 인 세부 사항에 의존 위반이있다. 우리는 프로젝트에있는 경우 질문은 그래서, 나는 모든 진실을 알고, 어떻게 그것을 할, 사용하지 않을 것을 기술하고 , 우리의 목적을 달성하지? (독자 질문 : WTF, 어떻게 가능합니까?)

     우리가하고 싶은 기능에 따라, 우리는 무엇을 타사의 패키지에 의존하지 않는, 새 프로젝트를 만들고 권한 세입자에 따라 인증 및 사용자의 권한을 인수하는 시스템을 서비스 할 , 우리는 새로운 하나의 생성 LoginUserE, 로그인 대표하는 DoVerify인증 프로세스를 수행을, 우리는 희망을 비밀 계정을 방문 할 때, 것을 의미 통화 암호화 시스템에 의해 결정 LoginUserE이 지역이해야 할 수도 있습니다 EncryptWay얻을 수는 EncryptHelper,의 새를 만들 수loginUser.go

package domain

type LoginUserE struct {
    Username     string
    IsLock       bool
    UniqueCode   string
    Mobile       string
    canLoginFunc func() bool
    EncryptWay
}

func (user *LoginUserE) CanLogin() bool {
    var can bool
    if user.canLoginFunc != nil {
        can = user.canLoginFunc()
    } else {
        can = !user.IsLock
    }
    return can
}

func (user *LoginUserE) DoVerify(sourceCode string, encryptedCode string) (bool, error) {
    if !user.CanLogin() {
        return false, errors.New("can not login")
    }
    match := user.EncryptHelper().Match(sourceCode, encryptedCode)
    return match, nil
}

     문제는 여기에 있다는 것입니다 EncryptHelper()이 방법은, 우리는, MD5는했다, 그것은 다른 패키지에 의존 할 필요가해야하는 암호화 방법을 알고 있지만, loginUser.go우리는 모순을 입력 한 것으로 보인다 타사 패키지에 의존하고 싶지 않아요. 테어 코크 육각형 구조를 제안, 내부 도메인의 핵심이며, 다른 종속 인터페이스를 통해 통신 하고 말하면, 도메인 층 인터페이스 인프라 층 (레이어 기술) 인터페이스를 구현합니다 정의 우리는를 정의 EncryptHelper하는 인터페이스를

type EncryptHelper interface {
    Encrypt(password string) string
    Decrypt(password string) string
    Match(source, encryptedString string) bool
}

     새로운 인프라의 그런 기본 계층 encrypt.go이 클래스의 구현

type MD5Way struct{}

func (md5 MD5Way) Match(source, encryptedString string) bool {
    return md5.Encrypt(source) == encryptedString
}

func (MD5Way) Encrypt(password string) string {
    data := []byte(password)
    md5Bytes := md5.Sum(data)
    return string(md5Bytes[:])
}

func (MD5Way) Decrypt(password string) string {
    panic("not support")
}

      어떤 문제는 해결 될 수 없다 구현 클래스베이스 레이어, 어떻게, 같은 시간 영역 계층 직접 의존하지 않고 할뿐만 아니라 그것을 사용할 수 있나요? 가장 좋은 방법은 실제로 의존성 주입입니다 만, 의존성 주입의 도입은 또 다른 역설에 빠진 -이, 의존성 주입 한 다음,이 지점 아래에 탐구 계속 나를 보자, 기술과 같이 요약 할 수있는 모든 기술적 인 세부 사항에 의존하지 않는 내 의존성 주입하는 방법을 달성하기 위해 않습니다. 에서 loginUser.go우리는 글로벌 변수를 생성var EncryptMap = make(map[EncryptWay]EncryptHelper)

var EncryptMap = make(map[EncryptWay]EncryptHelper)

func (encryptWay EncryptWay) EncryptHelper() EncryptHelper {
    if helper, ok := EncryptMap[encryptWay]; ok {
        return helper
    } else {
        panic("can not find helper")
    }
}

func AddEncryptHelper(encryptWay EncryptWay, helper EncryptHelper) {
    EncryptMap[encryptWay] = helper
}

     코어 층보다 클래스 관통 AddEncryptHelper해당 등록 EncryptHelper이 디자인은 여전히 첫눈 될 수 있지만, 코드의 증가 된 유지 비용을 초래할 것이다이 방법을 사용하여 다음 추가 필드와 함께 프로젝트 일단 의존성 주입 실제로 방패 구체적인 구현 클래스를 구축하는 과정 도메인 층의 여부로 변화는 프로젝트에 따라 다릅니다. 더 좋은 방법은 코멘트 영역에 오신 것을 환영합니다.

서비스 지역

     당신이 새로운 또는 도메인 중심의 설계에 관여 한 적이 있다면, 당신의 마음은 상대적으로 같은 고정됩니다 우리는 도메인 중심의 설계가 필요한 이유를 우리가 어떻게 기능을 생각해야합니다, 당신은 핵심으로 데이터 테이블 디자인을 분석하는 데 익숙해 질 시간이 오래 테이블의 건설. 나는 위의 기능을 설명하는 과정에서, 당신에 대해 생각하기 시작, 확신 어떻게 절대적으로 테이블에서 어떻게 어떻게 다시 현장을이 테이블 디자인, 데이터베이스, 기술 생각 납치 하지만, 또한 문제는 우리가 항상이 문제는 서비스 분야에서 해결된다, 결국이 문제의 데이타베이스를 해결할 필요가있다.
또 다른 문제는 현장 서비스 솔루션이다 논리를 조립 , 예를 들어, LoginUserE.DoVerify따라서 타사 패키지 또는 같은 수준의 다른 클래스에 의존하지 불구하고, 그러나 그의 상원에 우리가 분리에 의존이 다른 도메인의 참여에 의존 할 수있다 우리는이 층을 조립하는 영역을 서비스 할 필요가있다.
착륙을 달성하는 방법을 우리가 정의하는 모든의 처음에 대해 우리가 말하는 LoginCmd대로 Login상원에,

//implemention will show right behind this
func (service *LoginService) Login(loginCmd common.LoginCmd) (string, error) 

type LoginCmd struct {
    Username         string
    TenantId         string
    EffectiveSeconds int
    Mobile           string
    SourceCode       string
    LoginWay         string
    EncryptWay       string
}

     다음은 데이터베이스의 설계, 우리는 사용자가 다음 질문은, 우리가 직접 데이터베이스 기술에 의존 할 수있다, 존재 여부를 확인하는 찾기 위해 필요하지만, 우리가 필요로하는 장소에있는 Zezheng 수? 이와 유사하게, 물론, 인터페이스의 정의

type LoginUserRepo interface {
    GetOne(username, tenantId string) *domain.LoginUserDO
}

그러나 다시 여기에 의존성 주입 장소 위의 논의에, 나는 여전히 의존성 주입을 사용하지 않은,하지만 난 개인적으로의 사용을 권장, 단순 여기입니다

var loginService *LoginService
type LoginService struct {
    LoginUserRepo
}
func NewLoginService(repo LoginUserRepo,) *LoginService {
//do not argue to use double check lock,it's a example and does not hurt anyway
    if loginService == nil {
        return &LoginService{
            LoginUserRepo: repo,
        }
    } else {
        return loginService
    }
}

     그래서 우리 초기화 LoginService, repoImpl 시간을 전송 따라 격리 목적에 도달한다. 현장 서비스는 난 단지 돌봐 가지고, 진짜 목적을 한 나는 그 결과, 인터페이스의 정의는 여기 또한, 모든 스토리지가 기본이되는 데이터베이스가 사용되는지 모르고 더 의미 알 필요가 없습니다.

      위로 GetOne(username, tenantId string) *domain.LoginUserDO이 방법 또한 데이터 객체 클래스는 오히려 서비스보다, 도메인 층에 정의되어, 지점에가 노출되지만,베이스, 나는 도메인 계층은 데이터베이스 기술에 의존하지 않기 때문에,이되지 않는 것입니다 점 얽힌 적이 없습니다 데이터 객체 신경 안, 데이터 객체는베이스 층 아래에보다 적절한 배치되지?

     이제 이유는 데이터 객체 도메인 층에 있기 때문에

1.domain核心层不直接依赖其他层,如果DataObject放在base层势必违背这点;
2.domain层作为接口定义者,有权根据他自身的需求定义他想要的存储内容,其他层只需要服从并且实现。

     동시에, 우리는 E에서 이동 DO, DO에 cmd를에서, 코드 변환의 거대한 숫자를 원하지 않는다, 그래서 우리는 추출 할 dto.go파일, 콘서트를 저장하는 데 사용되는 코드입니다. 아래의 최종 코드 형태.

func (service *LoginService) Login(loginCmd common.LoginCmd) (string, error) {
    userDO := service.GetOne(loginCmd.Username, loginCmd.TenantId)
    userE := common.ToLoginUserE(*userDO)
    userE.EncryptWay = domain.EncryptWay(loginCmd.EncryptWay)

//login way contains PASSWORD and SMS ,encryptCode()is to get which one to be verify ,so userE will not to care about which way is exactly by logining
    encryptCode := service.encryptCode(loginCmd.LoginWay, userDO)
    if _, err := userE.DoVerify(loginCmd.SourceCode, encryptCode); err != nil {
        return "", err
    }

    //todo add login event and callback
    return service.token(userE.UniqueCode, loginCmd.EffectiveSeconds), nil
}

func (service *LoginService) encryptCode(way string, userDO *domain.LoginUserDO) string {
    switch way {
    case "PASSWORD":
        return userDO.Password
    case "SMS":
        return service.FindSmsCode(userDO.Mobile)
    default:
        panic("unknown login way")
    }
}

      새로운 폭풍이 다시 등장 service.token(userE.UniqueCode, loginCmd.EffectiveSeconds)이 논리 천천히 내가 설명 할 필요가있을 것으로 나타났습니다 위의 의미하는 것입니다. 성공 후 정상적인 검사에서 토큰을 부여 할 필요성을 방문하지만, 토큰은 다른 JWT는 도메인에 대해 우려하지 않아야 것과 기술적 인 세부 사항을 생성, 또는, 그래서 우리는 loginService 필드의 유형의 기능을 추가 제공

type LoginService struct {
    LoginUserRepo
    token func(uniqueCode string, effectiveSeconds int) string
}

func NewLoginService(repo LoginUserRepo, token func(uniqueCode string, effectiveSeconds int) string) *LoginService {
    if loginService == nil {
        return &LoginService{
            LoginUserRepo: repo,
            token:         token,
        }
    } else {
        return loginService
    }
}

최종 결과

     어떤 편차를 복사하지 기술적 인 세부 사항을 차폐, 비즈니스 로직, 재사용 가능한 자기 밀봉 비즈니스 모델로 구성 재사용 가능한 비즈니스 로직의 궁극적 인 목표를 강조했다. 결국 우리는 좋은 모델을 만들었습니다.

     이 비즈니스 모델에 관계없이 모든 기술적 프레임 워크, 모든 웹 프레임 워크, 또는 다른 장면에 노출, 데이터베이스 기술의 그들의 선택에 관계없이 파괴되지 않습니다, 그것은 모델에 영향을 미치지 않습니다. 외부 기술적 인 세부 사항이 문서는 모델에 영향을 미치지 않았다 자신의 결정에 의해 그것을 할 기술을 모델 선택을 구축에 초점을 맞추고, 실현 여기에 모든 사람을 따르지 않습니다.

테스트 분야를 더 완벽하게 드라이브를 드라이브

     밀교 아래로 전체 장, 이러한 경험이다, 대답은 나도 몰라, 아니 잘 확립 된 규범을 준수하는 없습니다 분리에 의존하지만 테스트 주도 동작을 수행하면, 다음은 무엇을 생각하도록 강요합니다 에 의존하지 않는 것이 의존, 모든 제 3 자 의존, 우리는 모의로 교체해야하기 때문에 이 파일이 디렉토리에 존재 조롱하는 이유입니다. 시험은 잘 쓰여진, 그리고 더 많은 문제가 없습니다.

개요

     이 코드의 품질을 향상, 다시 나는 파트 III가 좋은 코드, 그린 찬양 후계자를 작성, 더 많은 사람들이 더 쉽게 이해하고 도메인 기반 설계의 실천 할 수 있도록하기위한 기사의 분야 중심의 디자인 관련 쓴 살펴입니다 .

     도로는 긴 찬양 노래의 어떤 점을 말해 서있다!

     저자 : PLZ 빨간색 스카프 나에게 전화

     출처 : 도메인 중심의 모범 사례 - 도메인 기반 설계하는 방법을 당신에게 코드를 사용

    그렇지 않으면 법적 책임을 추구 할 수있는 권리를이 블로그를 다시 인쇄하지만,이 섹션에 의해 선언 된 저자의 동의가 유지되어야하지 않고, 및 기사 페이지의 명백한 위치에 원래 연결을 제공에 오신 것을 환영합니다.

추천

출처www.cnblogs.com/xxzhuang/p/11498939.html