這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。## 什麼是整潔架構?在《Clean Architecture: A Craftsman’s Guide to Software Structure and Design》一書中,著名作家 Robert “Uncle Bob” Martin 提出了一種具有一些重要特性的體繫結構,如架構、資料庫和介面的可測試性和獨立性。整潔架構的約束條件是:* 獨立的架構。該體繫結構並不依賴於某些帶有特性的軟體庫的存在。這允許您使用這些架構作為工具,而不是將您的系統束縛在有限的約束中。* 可測試的。商務規則可以在沒有 UI、資料庫、Web 服務器或任何其他外部元素的情況下進行測試。* 獨立的 UI 。UI 可以很容易地更改,而不會改變系統的其他部分。例如,可以用控制台 UI 替換 Web UI,而不需要更改商務規則。* 獨立的資料庫。您可以將 Oracle 或 SQL Server 替換為 Mongo、BigTable、CouchDB 或其他資料庫。您的商務規則不綁定到資料庫。* 獨立的任意外部代理。事實上,你的商務規則根本就不用瞭解外部的構成。瞭解更多請查看 : https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html因此,基於這些約束,每一層都必須是獨立的和可測試的。從 Uncle Bob 的架構中,我們可以將代碼分成 4 層:* 實體: 封裝企業範圍的商務規則。Go 中的實體是一組資料結構和函數。* 用例: 這個層中的軟體包含應用程式特定的商務規則。它封裝並實現了系統的所有用例。* 控制器: 該層中的軟體是一組適配器,它將資料從最方便的用例和實體轉換為最方便的外部代理,例如資料庫或 Web。* 架構和驅動程式: 這個層通常由架構和工具(如資料庫、Web 架構等)組成。## 使用 Golang 構建整潔架構讓我們以 user 包為例:```ls -ln pkg/user-rw-r — r — 1 501 20 5078 Feb 16 09:58 entity.go-rw-r — r — 1 501 20 3747 Feb 16 10:03 mongodb.go-rw-r — r — 1 501 20 509 Feb 16 09:59 repository.go-rw-r — r — 1 501 20 2403 Feb 16 10:30 service.go```在 entity.go 檔案中,我們有自己的實體 :```go//User datatype User struct {ID entity.ID `json:"id" bson:"_id,omitempty"`Picture string `json:"picture" bson:"picture,omitempty"`Email string `json:"email" bson:"email"`Password string `json:"password" bson:"password,omitempty"`Type Type `json:"type" bson:"type"`Company []*Company `json:"company" bson:"company,omitempty"`CreatedAt time.Time `json:"created_at" bson:"created_at"`ValidatedAt time.Time `json:"validated_at" bson:"validated_at,omitempty"`}```在 repository.go 檔案中我們定義存放庫的介面,用於儲存儲存實體。在這種情況下,存放庫意味著 Uncle Bob 架構中的架構和驅動層。它的內容是:```gopackage userimport "github.com/thecodenation/stamp/pkg/entity"//Repository repository interfacetype Repository interface {Find(id entity.ID) (*User, error)FindByEmail(email string) (*User, error)FindByChangePasswordHash(hash string) (*User, error)FindByValidationHash(hash string) (*User, error)FindAll() ([]*User, error)Update(user *User) errorStore(user *User) (entity.ID, error)AddCompany(id entity.ID, company *Company) errorAddInvite(userID entity.ID, companyID entity.ID) error}```該介面可以在任何類型的儲存層中實現,如 MongoDB、MySQL 等。在我們的例子中,我們使用 MongoDB 來實現,就像在 mongodb.go 中看到的那樣:```gopackage userimport ("errors""os""github.com/juju/mgosession""github.com/thecodenation/stamp/pkg/entity"mgo "gopkg.in/mgo.v2""gopkg.in/mgo.v2/bson")type repo struct {pool *mgosession.Pool}//NewMongoRepository create new repositoryfunc NewMongoRepository(p *mgosession.Pool) Repository {return &repo{pool: p,}}func (r *repo) Find(id entity.ID) (*User, error) {result := User{}session := r.pool.Session(nil)coll := session.DB(os.Getenv("MONGODB_DATABASE")).C("user")err := coll.Find(bson.M{"_id": id}).One(&result)if err != nil {return nil, err}return &result, nil}func (r *repo) FindByEmail(email string) (*User, error) {}func (r *repo) FindByChangePasswordHash(hash string) (*User, error) {}func (r *repo) FindAll() ([]*User, error) {}func (r *repo) Update(user *User) error {}func (r *repo) Store(user *User) (entity.ID, error) {}func (r *repo) AddCompany(id entity.ID, company *Company) error {}func (r *repo) AddInvite(userID entity.ID, companyID entity.ID) error {}func (r *repo) FindByValidationHash(hash string) (*User, error) {}```service.go 檔案表示 Uncle Bob 定義的用例層。在檔案中,我們有 Service 介面和它的實現。Service 介面是:```go//Service service interfacetype Service interface {Register(user *User) (entity.ID, error)ForgotPassword(user *User) errorChangePassword(user *User, password string) errorValidate(user *User) errorAuth(user *User, password string) errorIsValid(user *User) boolGetRepo() Repository}```最後一層,我們架構中的 Controller 是在 api 的內容中實現的:```cd api ; tree.|____handler| |____company.go| |____user.go| |____address.go| |____skill.go| |____invite.go| |____position.go|____rice-box.go|____main.go```在以下代碼中,從 api/main.go 中我們可以看看如何使用這些服務:```gosession, err := mgo.Dial(os.Getenv("MONGODB_HOST"))if err != nil {elog.Error(err)}mPool := mgosession.NewPool(nil, session, 1)queueService, err := queue.NewAWSService()if err != nil {elog.Error(err)}userRepo := user.NewMongoRepository(mPool)userService := user.NewService(userRepo, queueService)```現在我們可以輕鬆地建立包測試,比如:```gopackage userimport ("testing""time""github.com/thecodenation/stamp/pkg/entity""github.com/thecodenation/stamp/pkg/queue")func TestIsValidUser(t *testing.T) {u := User{ID: entity.NewID(),FirstName: "Bill",LastName: "Gates",}userRepo := NewInmemRepository()queueService, _ := queue.NewInmemService()userService := NewService(userRepo, queueService)if userService.IsValid(&u) == true {t.Errorf("got %v want %v",true, false)}u.ValidatedAt = time.Now()if userService.IsValid(&u) == false {t.Errorf("got %v want %v",false, true)}}```使用整潔的體繫結構,我們可以將資料庫從 MongoDB 更改為 Neo4j ,而不會破壞應用程式的其他部分。這樣,我們可以在不損失品質和速度的情況下開發我們的軟體。## 引用- https://hackernoon.com/golang-clean-archithecture-efd6d7c43047- https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
via: https://medium.com/@eminetto/clean-architecture-using-golang-b63587aa5e3f
作者:Elton Minetto 譯者:SergeyChang 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
978 次點擊 ∙ 1 贊