web-technical-blog

web開発に関する技術メモ

パッケージ構成

パッケージ構成は、大まかに分けて3種類...

One Package

  • Repository自体を単一のパッケージとみなす
  • Coverageが取りやすい
  • Libraryなど、簡素な構成で済むものに向いている
.
├── ctx.go
├── debug.go
├── error.go
├── handler.go
├── handler_func.go
├── jsonrpc.go
├── method.go
└── parse.go

Flat Package

  • 各パッケージの責務を明確にし、分割を行う
  • 機能ごとに分割しているイメージ
  • Go言語の標準パッケージの構成に親しい考え方
  • 階層を掘ったとしても、2階層ぐらいで落ち着く
  • パッケージの責務を明確にし、お互いに疎結合を心がける
  • Micro Serviceや、Middlewareなどに向いている
.
├── ctx
│   └── ctx.go
├── debug
│   └── debug.go
├── error
│   └── error.go
├── handler
│   ├── handler.go
│   └── handler_func.go
├── jsonrpc.go
├── method
│   └── method.go
└── parse
    └── parse.go

Multiple Packages

.
├── controller
│   ├── debug
│   │   └── debug.go
│   └── handler.go
├── jsonrpc.go
├── model
│   ├── ctx
│   │   └── ctx.go
│   ├── error
│   │   └── error.go
│   ├── marshal
│   │   └── unmarshal.go
│   └── method
│       └── method.go
└── view
    └── index.html.tpl

DDD(ドメイン駆動設計) Domain-Driven Design

  • 顧客と開発者が業務を戦略的に理解し、共通の言葉を使いながらシステムを発展させる手法
  • DDDでは大規模な1つのシステムとデータベースで構築するのではなく、業務にとって適切な独立したシステムに分割する

レイヤードアーキテクチャ

  • ドメインロジックと、それ以外(DBやViewなど)を切り離すのが目的

Lightweight DDD

src/
└── myapp
    ├── application
    ├── domain
    ├── infrastructure
    ├── interfaces
    └── library
  • 4つのLayerで構成

    • Application
    • Domain
    • Infrastructure
    • Interface(s)
  • Application Layer

    • Domain layerの処理をまとめInterface Layerに提供
    • Flat Packagesの思想でパッケージ分割
    • interパッケージに活用し、共通ロジックを隠蔽
  • Domain Layer
    • Domain Layerは、他Layerには依存しない
    • Entityや、Enum,Interfaceを提供
  • Infrastructure Layer
    • GAEのライブラリをWrap
    • 基礎的な処理を保持
      • Logging
      • Validation Rule
      • World Filtering
      • Value Get/Set in Context
      • Persistence
  • Interface Layer
    • JSON-RPCのHandler群
      • Search.FindProducなど
    • Original Error定義
      • 32044:Not Foundなど
    • Interceptor群
      • Contextに色々詰める
      • Action Loggerなど
    • その他
      • Startup処理、状態取得

最適なパッケージ構成

相互参照せずに快適に開発を進められるパッケージ構成を見つけるのは難しい

第1ケース

プロジェクトルートに展開する

.
├── db.go
├── errors.go
├── handler.go
├── main.go
└── model.go

概要

  • ほぼ全てのファイルをプロジェクトルートに展開するやり方、mainパッケージに集約する
  • utlity的な共通処理も、構造体もinterfaceもその実装も、全て同じ階層にあれば、パッケージを切る必要がない

特徴

  • CLIツールや小さめのサーバー、あるいは小さめのパッケージなどには適している

デメリット

  • 中規模以上のAPIサーバーを用意するとなると厳しい
  • 20個以上とかファイルが乱立すると分かりにくくなる

第二ケース

MVC

.
├── main.go
├── clients
├── config
├── controllers
├── models
└── views
  • 他の言語のアプリケーションフレームワークを使用しているとよく見るやつ
  • あくまでもプロジェクトルートが綺麗なだけ

  • 例えば、modelsディレクトリの直下に行った時は、本当はこうしたい

.
├── main.go
├── clients
├── config
├── controllers
├── models
    ├── article
    └── user
└── views
  • リソースごとにパッケージを区切りたくなるが、これでは大体相互参照の問題にぶつかる
  • article.goとuser.goなど、構造体やそれに付随する実装を全てmodelsディレクトリ直下に展開するという流れがある

  • 小さめのAPIなら許容できなくいが、主要APIとして利用されるサーバーコードを書くときはリソースが多くなり、goのソースコードのファイルが多くなる

参考URL

https://speakerdeck.com/mercari/ja-golang-package-composition-for-web-application-the-case-of-mercari-kauru