dongxiejie9387 2016-07-06 12:15 采纳率: 100%
浏览 24

“ Clean Architecture” Go程序的通用ID类型

I am trying to find a proper type for my IDs in a Go program designed using Uncle Bob Martin's "Clean Architecture".

type UserID ...

type User struct {
  ID UserID
  Username string
  ...
}

type UserRepository interface {
  FindByID(id UserID) (*User, error)
  ...
}

I am following Uncle Bob Martin's "Clean Architecture", where the code is organized as a set of layers (from outside-in: infrastructure, interfaces, usecases, and domain). One of the principles is the Dependency Rule: source code dependencies can only point inwards.

My User type is part of the domain layer and so the ID type cannot be dependent on the database chosen for the UserRepository; if I am using MongoDB, the ID might be an ObjectId (string), while in PostgreSQL, I might use an integer. The User type in the domain layer cannot know what the implementing type will be.

Through dependency injection a real type (e.g. MongoUserRepository) will implement the UserRepository interface and provide the FindByID method. Since this MongoUserRepository will be defined in the interfaces or infrastructure layer, it can depend on the definition of UserRepository in the (more inward) domain layer.

I considered using

type UserID interface{}

but then the compiler will not be very helpful if code in one of the outer layer tries to assign in incorrect implementation type.

I want to have the interfaces layer or infrastructure layer, where the database is specified, determine and require the specific type for UserID, but I cannot have the domain layer code import that information, because that will violate the dependency rule.

I also considered (and am currently using)

type UserID interface {
    String() string
}

but that assumes knowledge that the database will use strings for its IDs (I am using MongoDB with its ObjectId -- a type synonym for string).

How can I handle this problem in an idiomatic fashion while allowing the compiler to provide maximum type safety and not violate the dependency rule?

  • 写回答

2条回答 默认 最新

  • dongpu8935 2016-07-06 13:08
    关注

    Maybe you can use something like this:

    type UserID interface {
      GetValue() string
      SetValue(string)
    }
    

    Then you assume that you are always passing and getting string as an ID (it can be stringified version of the integer ID in case of PgSQL and other RDBMSs), and you implement UserID per database type:

    type PgUserID struct {
        value int
    }
    
    func (id *PgUserID) GetValue() string {
        return strconv.Itoa(id.value)
    }
    
    func (id *PgUserID) SetValue(val string){
        id.value = strconv.Atoi(val)
    }
    
    type MongoUserID struct {
        value string
    }
    
    func (id *MongoUserID) GetValue() string {
        return value
    }
    
    func (id *MongoUserID) SetValue(val string){
        id.value = val
    }
    

    I wonder if this accomplishes what you want to achieve, but maybe it's more elegant to hide string conversions inside the UserID?

    评论

报告相同问题?

悬赏问题

  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算