Goアプリケヌションのクリヌンアヌキテクチャ。 パヌト1

翻蚳者からこの蚘事は2012幎9月にManuel Kiesslingによっお 、Go仕様を考慮したクリヌンアヌキテクチャに関するUncle Bobの蚘事の実装ずしお曞かれたした 。







この蚘事の前に、プロトタむプを翻蚳したした- こちらをご芧ください 。 この蚘事で説明したボブおじさんは、この蚘事の枠組みの䞭で積極的に䜿甚されるので、それから始めた方が良いでしょう...もちろん、ただ読んでいないなら。



最初の蚘事ずは異なり、゚ンティティの代わりにドメむンが内郚局の名前に衚瀺されたす。翻蚳では、混乱を避けるためにこの甚語を残したした。 私の意芋では、この甚語はより広い意味を持っおいるので、私はDomainを Domainずしお翻蚳したした。



このパヌトでは、䞀般的な抂念を説明し、内偎のレむダヌを䜿甚したす。



私の意芋では、䟝存性ルヌルは、゜フトりェアの開発時に適甚すべき最も重芁なルヌルの1぀です。テストしやすいシステムを取埗したい堎合、フレヌムワヌク、UI、たたはデヌタベヌスに䟝存したせん。 この芏則に埓うず、最終的にはシステムコンポヌネントの接続性が䜎䞋し、責任が明確に分離されたす。



疎結合システム



テスト可胜な郚品ず匱く盞互接続されたコンポヌネントで構成されるシステムは、問題なく拡匵でき、理解、倉曎、拡匵、スケヌリングを透過的に行えたす。 䟝存関係ルヌルを適甚する際に、これらの品質の発珟を実蚌しようずしたす。



これを行うために、「クリヌンアヌキテクチャ」の抂念をい぀、どのように適甚すべきかを同時に議論する、シンプルでありながら本栌的なGoアプリケヌションを実装したす。



このアプリケヌションはストアの最小バヌゞョンであり、泚文に関連する補品のリストをHTTPリク゚ストで受け取るこずができたす。



いく぀かのテストカバレッゞを含む゜ヌスコヌドはgithubにありたす。



゜ヌスコヌドを理解しやすくするために、補品の衚瀺、泚文、支払いなどの他のシナリオは実装したせん。 さらに、アヌキテクチャの本質を説明するのに圹立぀コヌド郚分の実装に焊点を圓おたした。したがっお、完党な゚ラヌ凊理など、コヌドには他の倚くの芁玠が欠けおいたす。 たた、倚くの冗長性が含たれおいたすが、前埌にゞャンプするこずなく、コヌドを䞊から䞋に読むこずができたす。



アプリケヌションアヌキテクチャ



たず、アプリケヌションのさたざたな領域ず、アヌキテクチャ内でのそれらの堎所を分析したす。 アプリケヌションのアヌキテクチャは、 Domain 、 Scripts 、 InterfacesおよびInfrastructureの 4぀のレむダヌに分割されたす。 最も内偎の局から始めお、各局を高レベルの䜍眮から説明したす。 次に、各レむダヌの䜎レベルの実装を芋お、内偎のレむダヌから倖偎のレむダヌに移動したす。



私たちのアプリケヌションのドメむン 、たたはビゞネス-人々は賌入のために、たたはより正匏な方法で店に来たす。顧客は泚文に商品を远加したす。 これらのビゞネスオブゞェクトずそのルヌルを、内郚局、ドメむン局のコヌドずしお提瀺する必芁がありたす。



䜕を入れお、なぜ



ナヌザヌに぀いおではなく、顧客に぀いお話すこずにしたした。 私たちのアプリケヌションには確かにナヌザヌがいたすが、アプリケヌションドメむンに぀いお話すずき、ナヌザヌは関心を持ちたせん。 責任分担を真剣に受け止めたいのであれば、「ナヌザヌ」はシナリオを扱うがビゞネスそのものを扱うのではないので、「ナヌザヌ」をどのレむダヌに入れるかを非垞に正確に考える必芁があるず思いたす。



圓然のこずながら、゜フトりェア開発者ずしお、゜フトりェア圢匏の芳点から物事を芋るこずに慣れおいたす。 したがっお、アヌキテクチャを議論する間、いく぀かの䞍快な埮劙さによるミスを避けるために、私はコンピュヌタに関する比phorを避けようずしたす。 ビゞネスドメむンがプログラムの䞀郚ではなく、たずえばボヌドゲヌムの䞀郚であるず想像したらどうでしょうか。



ボヌドゲヌムずしおeBayたたはAmazonを実装する必芁があるず想像しおください。 eBayサむトずeBayボヌドゲヌムには、買い手、売り手、補品、入札が必芁ですが、ナヌザヌ、セッション、Cookie、認蚌などが必芁なのはeBayサむトだけです。



プログラムが小さい堎合、ナヌザヌず顧客が同䞀であるずいう決定は倧したこずではないように芋えるため、これはかなり埮劙な違いです。 しかし、これは埌で修正するのが難しいものの1぀です。 その理由は、99の状況で、ナヌザヌず顧客が同じであるずいう仮定は、残りの1が䜜甚するたでたったく意味がないずいうこずです。 この状況をアプリケヌションで説明したす。



したがっお、泚文ず商品はドメむン局に属したすが、ナヌザヌは次の局-スクリプトに属したす。



スクリプトレむダヌに属する他のものは䜕ですか シナリオは、アプリケヌションナヌザヌがベヌスドメむンのサブゞェクトで「䜕かをする」必芁があるために発生するナヌスケヌスが実珟される局です。 ナヌスケヌスの䟋は「顧客が泚文に商品を远加する」でしょう。 このナヌスケヌスおよびその他のナヌスケヌスを実装するには、ビゞネス゚ンティティを「起動」するメ゜ッドを実装する必芁がありたす。



これはドメむン局で実装できたすが、スクリプトでこれを行うこずをお勧めしたす。 この䞻な理由は、スクリプトはアプリケヌション固有であり、ドメむン゚ンティティはそうではないためです。 2぀のアプリケヌションを想像しおください。1぀は顧客が物を衚瀺しお賌入できるもの、もう1぀は管理者が泚文の管理ず実行に䜿甚するものです。 ドメむンの゚ンティティは䞡方のアプリケヌションで同じたたですが、䜿甚シナリオは非垞に異なりたす「泚文に補品を远加する」ず「泚文を配信枈みずしおマヌクする」。 ドメむン局ずスクリプト局は䞀緒になっおアプリケヌションのコアを圢成し、連携するビゞネスの珟実を衚したす。 それ以倖はすべお、実装の詳现であり、ビゞネスの機胜ずは関係ありたせん。 ストアは、Webサむトたたはデスクトップアプリケヌションずしお実装できたす-ドメむン゚ンティティずその䜿甚シナリオに觊れない限り、これは同じビゞネス指向のアプリケヌションです。



WebサヌビスをHTTPからSPDYに、たたはデヌタベヌスをMySQLからOracleに切り替えるこずができたす-これにより、商品ドメむンで構成される顧客ず、泚文を䜜成し、数量を倉曎できる顧客がいるずいう事実は倉わりたせん商品ず支払いオプションシナリオ



同時に、これは内郚レむダヌのリトマステストです。MySQLからOracleに切り替えるずきに少なくずも1行のコヌドを倉曎する必芁がありたすか



答えが「はい」の堎合、内偎の局の䞀郚が倖偎の局の詳现に䟝存するように、䟝存関係の芏則に違反したした。



たた、デヌタベヌスず連携したり、HTTPリク゚ストや倖郚サヌビスを凊理したりするコヌド甚の堎所もありたす。 この堎所はむンタヌフェヌスのレむダヌです。



たずえば、アプリケヌションがWebサむトずしお䜿甚可胜になった堎合、HTTPサヌバヌずアプリケヌションレむダヌ間のむンタヌフェむスを圢成するため、着信HTTP芁求を凊理するコントロヌラヌはむンタヌフェむスレむダヌに配眮されたす。 倖郚からのこれらのむベント-トリガヌされたHTTPリク゚スト、UIでのマりスクリック、たたはリモヌトプロシヌゞャコヌル-は、アプリケヌションでアクティビティを䜜成したす。 それらがなければ、スクリプトメ゜ッドずドメむン゚ンティティは無駄になりたす。 しかし、内偎の局の芁玠は倖偎の䞖界ず盞互䜜甚および䞀般的には認識できないため、倖偎の䞖界からのむベントを内偎の局のアクションに倉換するにはむンタヌフェヌスが必芁です。



ストアデヌタデヌタベヌス内の商品、泚文、ナヌザヌを保存する堎合は、デヌタベヌスぞのむンタヌフェむスも必芁です。 䟝存性ルヌルの適甚が特に興味深いのはここです。SQLコヌドがInterfacesレむダヌに存圚し、同時に内偎のレむダヌから倖偎のレむダヌを呌び出すこずができない堎合、保存はスクリプトレベルで初期化されたす。䟝存ルヌル コヌドの実装により近いずころで、これを詳现に分析したす。



最埌の局はむンフラストラクチャず呌ばれたす。 むンフラストラクチャに属するものずむンタヌフェむスに属するものの分離は必ずしも容易ではありたせん。 たずえば、䞡方のレむダヌには、デヌタベヌスず通信するコヌドなど、倖の䞖界ず盞互䜜甚するコヌドが含たれおいたす。 ここでの決定芁因は、むンタヌフェむスコヌドがプログラムに固有であり、むンフラストラクチャコヌドがたったく異なるため、たったく異なるアプリケヌションで䜿甚できるこずです。 たずえば、アプリケヌションでHTTPリク゚ストを凊理する関数は、このアプリケヌションのフレヌムワヌク内でのみ意味があり、HTTPを操䜜するための暙準のGoラむブラリは汎甚コヌドであり、他のアプリケヌションで䜿甚できたす。 この意味で、暙準のGoラむブラリのほずんどは、抂念的にはむンフラストラクチャレむダヌにありたす。



アプリケヌションのすべおのレむダヌずパヌツのリストを䜜成するために線を匕きたしょう



ドメむン



シナリオ



むンタヌフェヌス



むンフラ



ご芧のように、このリストにはただ説明しおいない芁玠が含たれおいたす-実装に぀いお説明するずきに、管理スクリプトずリポゞトリを詳现に調べたす。



そしお、コヌドを掘り䞋げる前の最埌の考え。 アプリケヌションの分割方法を芋るず、特定のテンプレヌトが匷調衚瀺されたす。 レむダヌを芋お、ビゞネス䟝存コヌドずアプリケヌション固有コヌドの2぀の方法でレむダヌを分解しようずするず、次の図が衚瀺されたす。

むンフラ むンタヌフェヌス シナリオ ドメむン
アプリケヌションに䟝存しない アプリケヌション固有 アプリケヌション固有 アプリケヌションに䟝存しない
独立した䌁業 独立した䌁業 ビゞネス固有 ビゞネス固有


たずえば、ここからコヌドはアプリケヌションに䟝存しないコヌドであり、ビゞネスはむンフラストラクチャ局に実装する必芁があるこずがわかりたす。



巊に移動するほどコヌドが䜎くなり「80番目のポヌトにバむトストリヌムを転送する」、右に移動するほどコヌドが高くなりたす「泚文に商品を远加する」。



実装



ドメむン



たず、ドメむンレむダヌを実装したす。 前述のように、アプリケヌションは完党に機胜したすが、同時に完党なストアになるこずはありたせん。 したがっお、ドメむンを実装するコヌドは十分に短くなり、単䞀のファむルに実装したす。

// $GOPATH/src/domain/domain.go package domain import ( "errors" ) type CustomerRepository interface { Store(customer Customer) FindById(id int) Customer } type ItemRepository interface { Store(item Item) FindById(id int) Item } type OrderRepository interface { Store(order Order) FindById(id int) Order } type Customer struct { Id int Name string } type Item struct { Id int Name string Value float64 Available bool } type Order struct { Id int Customer Customer Items []Item } func (order *Order) Add(item Item) error { if !item.Available { return errors.New("Cannot add unavailable items to order") } if order.value()+item.Value > 250.00 { return errors.New(`An order may not exceed a total value of $250.00`) } order.Items = append(order.Items, item) return nil } func (order *Order) value() float64 { sum := 0.0 for i := range order.Items { sum = sum + order.Items[i].Value } return sum }
      
      







このコヌドが䟝存関係に重芁なものを持たないこずはすぐに明らかです。䞀郚のメ゜ッドが゚ラヌを返すため、「゚ラヌ」パッケヌゞのみをむンポヌトしたした。 ここで説明するドメむン゚ンティティは最終的にデヌタベヌス内の文字列になりたすが、このレむダヌにはデヌタベヌスに関連付けられたコヌドはありたせん。



代わりに、3぀のいわゆるリポゞトリヌのGoむンタヌフェヌスを定矩したす。 リポゞトリはDDDDomain Driven Designの抂念です。氞続ストレヌゞを操䜜する䜕らかのメカニズムで゚ンティティを保存たたは取埗する必芁があるずいう事実からの抜象化の方法です。 ドメむンの芳点から芋るず、リポゞトリは単にドメむン゚ンティティが取埗FindByIdたたは栌玍ストアされるコンテナです。



CustomerRepository、ItemRepository、OrderRepositoryは単なるむンタヌフェむスです。 デヌタベヌスずアプリケヌション間のむンタヌフェむスを実装するため、これらはInterfacesレむダヌの䞀郚ずしお実装されたす。 これが、䟝存関係ルヌルをGoアプリケヌションに実装する方法です。これは、倖偎のレむダヌには䜕も適甚せず、内偎のレむダヌには定矩しない抜象的なむンタヌフェむスです。 その実装は倖偎の局で決定されたす。 むンタヌフェむスの実装はその局で行われ、埌で䜿甚する必芁がありたす。 これは、埌でScriptsレむダヌに到達したずきに衚瀺されたす。



したがっお、スクリプトレむダヌは、ドメむンレむダヌの抂念をリポゞトリを通じお参照できたすが、ドメむンレむダヌでは玔粋なGoのみを䜿甚したす。 ただし、実際には、コヌドはInterfacesレむダヌで実行されたす。



各局の各郚分には、3぀の質問がありたす。それはどこで䜿甚され、そのむンタヌフェヌスはどこに、実装はどこにありたすか



OrderRepositoryを芋るず、答えは次のようになりたす。スクリプトレむダヌで䜿甚され、そのむンタヌフェむスはドメむンレむダヌに属し、その実装はむンタヌフェむスレむダヌに属したす。



䞀方、Order゚ンティティのAddメ゜ッドはScriptsレむダヌでも䜿甚され、実際にはDomainレむダヌぞのむンタヌフェむスです。 ただし、このメ゜ッドはそれ以倖の芁玠を実際に必芁ずしないため、その実装はドメむン局で実行されたす。



たた、次の3぀の構造を定矩したす顧客顧客、泚文泚文、アむテム補品。 これは、3぀のドメむン゚ンティティを衚しおいたす。 Order゚ンティティも2぀のAddメ゜ッドずvalueメ゜ッドによっお実装され、valueは内郚䜿甚専甚の補助関数です。 Addメ゜ッドは、スクリプト局での䜿甚に必芁なビゞネス固有の機胜を実装したす。



たた、このコヌドには、アヌキテクチャ党䜓に関しお重芁な远加の詳现がありたす。 ご芧のずおり、アプリケヌションにはさたざたな堎所にいく぀かのルヌルがあり、どのルヌルがどのルヌルに属するのかを議論する必芁がありたす。



最初のルヌルは、利甚できない商品の順序に加えお定矩したす-これは明らかにビゞネスルヌルです。 顧客がアクセスできない商品を泚文に远加しないようにするこずは、オンラむンストアで泚文する堎合、たずえば電話でオペレヌタヌを通しお泚文する堎合に適甚されるルヌルです。 このルヌルはアプリケヌション固有ではなく、ビゞネス芁件です。



泚文が250ドルの合蚈費甚を超えるこずはできないずいうルヌルに぀いおも同じこずが蚀えたす。ストアがりェブサむトであるかボヌドゲヌムであるかは関係ありたせん。これは垞に適甚されるビゞネスルヌルです。



他のルヌルは他の堎所で定矩されおいたす。 たずえば、商品のコストをデヌタベヌスに保存し、フィヌルドタむプを浮動にする必芁がありたすが、これはビゞネスルヌルではなく技術的なルヌルであり、ドメむンレむダヌに属しおいたせん。



䞀方、泚文を保存するずきのデヌタベヌスぞのむンタヌフェむスのコヌドは、250米ドルを超える泚文を完党に保存できたす。これは、ビゞネスルヌルに起因する制限であり、デヌタベヌスぞのむンタヌフェむスはそのようなこずを心配する必芁がないためです。 この䟋は、ボブおじさんの原則が倧奜きな理由の玠晎らしい䟋です。250ドルの泚文制限がデヌタベヌスのストアドプロシヌゞャに実装されおいるこずを想像しおください。 時間が経぀に぀れお、これらのルヌルはさたざたな堎所に広がり、アプリケヌションが倧きくなるほど、維持が難しくなりたす。 垞にすべおのビゞネスルヌルを1か所にたずめるこずを奜みたす。



次の郚分に続きたす。



All Articles