キメラアーキテクチャーのすゝめ

このエントリーをはてなブックマークに追加

なぜキメラアーキテクチャー1がいいのか、どういったものがキメラアーキテクチャーと呼べるのか殴り書きしていきます。

アプリケーションエンジニアの属性上、静的型付け言語的な要素があるかもしれません。

キメラアーキテクチャーの特徴

レイヤーの責務が定まっていない

MVCやMVPなどアーキテクチャーと呼ばれるものはレイヤーを分けてレイヤーごとに責務(どういう処理を書くべきか)が分かれています。キメラアーキテクチャーではレイヤー分けはあるものの責務については定まっていません。

たとえば、ViewとViewModel、ViewModelとPresenterどちらにロジックを書いてもよいといった感じです。 DDDなどでドメインロジックなどを分割することがありますが、通信ロジック・データ保存ロジックはRepositoryに、ロジックはPresenterにといったようにロジックに対して1つの汎用レイヤーと特定のロジック向けのためのレイヤー分けがされているものはキメラアーキテクチャーにおける責務が定まっていないレイヤー分けとは呼ばれません。

キメラアーキテクチャーにおける責務が定まっていないレイヤー分けとは、本当にどちらのレイヤーに書いてもよい、この処理を書くレイヤーが複数あるという状態を指します。

副作用のある設計

キメラアーキテクチャーにおけるメソッドの実装は呼び出し側目線で考えなければいけません。呼び出し側が他のメソッドを呼び出した後に当該メソッドを呼ぶのなら、他のメソッドで行った処理を当該メソッドでしてはいけません。

また、呼び出し側目線で実装しなければいけないので、メソッドの内部でクラス・構造体に対する状態の変更を行っていても、それは呼び出し側目線では不必要な情報なので提示してはいけません。

public class Program
{
    public void Main(string[] args)
    {
        var repository = new Repository();
        repository.ReadLines();
        string firstLine = repository.GetFirstLine();
    }
}

public class Repository
{
    private string[] lines;

    public int LineCount => lines.Length;
    public bool IsEmptyFirstLine { get; private set; }

    public void ReadLines()
    {
        lines = File.ReadAllLines("PATH");
    }

    public string GetFirstLine()
    {
        IsEmptyFirstLine = lines[0] == string.Empty;
        return lines[0];
    }
}

例として挙げるとこんな感じでしょうか。

ソースコードの配置箇所は定まっていない

キメラアーキテクチャーではソースコードの配置箇所を定めていません。 MVCやMVPなどのアーキテクチャーではレイヤーごとにフォルダを作成し、その該当する配下にソースコードを配置するか、画面単位でソースコードを配置するかのどちらかですが、キメラアーキテクチャーではソースコードはルートフォルダ直下に並べて配置したり、それっぽいフォルダ名を作りその配下に配置していきます。たとえ、配置できるフォルダが複数あってもかまいません。

MVCやMVPのように以下のようなフォルダ階層はキメラアーキテクチャーではNGです:

---views
| |-activities (MainActivity.java, ...)
| \-fragments (LoginFragment.java, SplashFragment.java, ...)
|--models (LoginModel.java, SplashModel.java, ...)
|--repositories (ApiRepository.java, ...)
|--services (LanguageService.java, ...)
\--presenters (LoginPresenter.java, ...)

キメラアーキテクチャーでのフォルダ階層は以下のような感じです:

---views
| |-common (SplashFragment.java, ...)
| \-main (MainActivity.java, ...)
|--login (LoginFragment.java, LoginModel.java, LoginPresenter.java, ...)
|--repositories (ApiRepository.java, ...)
\--services (LanguageService.java, ...)

できる限り1つのファイルにまとめる

キメラアーキテクチャーではファイル数が増えるのを抑制するため、複数のクラスや構造体などを1つのファイルになるべくまとめるようにします。 たとえ、クラスを分割しファイルを分割したほうが処理の見通しが立ちやすくても、ファイル数が多ければ見通しはよくありません。

キメラアーキテクチャーではファイル数を少なくすることで、ソースコードの見通しを良くするアプローチを取っています。

他のアーキテクチャーを自称する

MVCやMVPなどは設計方法が幅広いアーキテクチャーでありますが、FluxやMVIやMVVMなどは各レイヤーの責務などがはっきりと示されているアーキテクチャーであります。

キメラアーキテクチャーは自らキメラアーキテクチャーであると名乗れないので、このようなFluxやMVVMなどを自称していく必要があります。ここで自称する際のポイントは自称するアーキテクチャー通りの設計にせず、そこからキメラアーキテクチャー通りに設計の修正をする必要があります。また、これらの修正は他の開発者にバレてはいけません、つまりREADMEなどにキメラアーキテクチャーの設計を書いてはいけないのです。

リフレクションで決め打ちする

リフレクションなどのメタプログラミングは適度に用いれば非常に強力な武器となります。

しかし、出し惜しみするのはもったいないのでキメラアーキテクチャーでは積極的に使っていきましょう。 また、プログラム上の名前は我々プログラマーが統治しているので、該当する名前がなかった時のフォールバック処理は無駄になります。そういった無駄な処理は記述してはいけません。

テストコードを書かない

テストコードを書いてコードの正確性を担保する開発思考がありますが、キメラアーキテクチャーではプログラマーを信頼しています。プログラマーが書くコードは絶対正しいので、テストコードを書く作業自体が無駄となってしまいます。そのため、無駄を良しとしないキメラアーキテクチャーではテストコードは書いてはいけません。

また、わざわざテストコードを書ける設計にするのもよくありません。DIなどでテストコードを書きやすくする手法がありますが、キメラアーキテクチャーでは採用しません。

名前付けはそのときの気分次第

特定のプログラミング言語やコミュニティ、アーキテクチャーなどではクラス・構造体・関数などにprefixやpostfixを付ける場合があります。 たとえば、C#ではasyncメソッドにAsync postfix、interfaceにI prefixを付けます。

キメラアーキテクチャーではプログラマーに気分良くコーディングしてもらうため、このような規則には縛られない開発を行います。名前付けはそのときの気分で決め、気分が変われば名前を変更すればよいのです。

使わなくなったViewの処理は残しておく

アプリケーションの改修で今まで使っていたソースコードが使わなくなるということがたびたび起こります。

キメラアーキテクチャーでは今後再び使うことになるかもしれないということを念頭にViewの表示だけオフにし、Viewのコードからなにもかもすべて残すようにします。このようにすることで再び使う場面に遭遇してもすぐに復活させることができるのです。

共通部分は基盤クラスを用意して共通化する

コード量が増えていくと、各クラスで共通処理が存在していくことがあります。

キメラアーキテクチャーでは、そのような共通処理部分が複数存在するとソースコードが増えてしまいますので、基盤クラスにまとめてしまい、その基盤クラスから各クラスへ派生するようにします。 この際、型の責務などを考えて共通化を躊躇してはいけません。共通化はソースコード短縮のために行っているので、基盤クラスにまとめれるものはすべて例外なくまとめてしまいましょう。

また、基盤クラスに共通化しまとめる際に、無駄な処理が増えてはいけません。基盤クラスのフィールドを派生クラスが知っていれば処理が増えることはありません。そのため、基盤クラスのフィールドはできる限りprotectedにしましょう。

複数クラスから使われるフィールドはpublicにする

複数クラスから使われるフィールドをいちいちgetメソッド経由で公開するのはソースコードが増えるだけで無駄となります。キメラアーキテクチャーではpublicフィールドとして公開することが推奨されます。

キメラアーキテクチャーのメリット

ここまでキメラアーキテクチャーの特徴をリストアップしてきましたが、それらの特徴から見えてくるメリットがあります。

  • 開発スピードの向上
  • ソースコードの最小化
  • 開発者への信頼
  • 設計の秘匿性

これらはスピード重視で定量的なアウトプットが必要とされる現場にぴったりの特徴ではないでしょうか。プログラマーに考えさせる時間を与えずただひたすらに書かせる開発こそ、進捗のはっきりしない業務を行うIT企業にうってつけかと思われます。

また、開発者への信頼によって成り立つキメラアーキテクチャーでは、開発者のスキルレベルに寄らないコードのアウトプットに向いています。近年エンジニア不足が叫ばれる中、未経験エンジニアや初心者エンジニアを即戦力で現場に派遣できるアーキテクチャーであり、今後の主流になっていくのではないでしょうか。


.

.

.

.

.

.

.

.

.

.

.

.

.


んなわけねーよww

ここからはキメラアーキテクチャーがいかに邪悪で駆逐すべき対象であるかについて理由をふまえて述べていきます。

キメラアーキテクチャーの邪悪性

可読性・保守性・改修性が低くなる、不具合を誘発させる

  • 副作用のある設計
  • リフレクションで決め打ちする
  • テストコードを書かない
  • 使わなくなったViewの処理は残しておく
  • 共通部分は基盤クラスを用意して共通化する
  • 複数クラスから使われるフィールドはpublicにする

これらに関係しますが、ソースコードをちょっと変えるだけで動かなくなる・プログラムがクラッシュするようになるなどの不具合を誘発させやすいコードになってしまう/なりやすくなります。また、可読性・改修性も悪くなってしまいます。

副作用のある設計リフレクションで決め打ちする複数クラスから使われるフィールドはpublicにする に関してはソースコードを変更することでダイレクトに影響を与えてしまう事柄なので、特に説明は必要ないかと思います。

副作用のあるコードがなぜダメなのかはいちおう以前書きましたので、よろしければどうぞ↓

副作用のあるコードはなぜいけないのか

副作用のあるコードは書かないほうがいいというものは経験上わかっていたのですが、今まではなぜ書いてはいけないのかのいい例が思いつかずあまり説明できていなかったのですが、いい例を思いついたので書いておきます。

blog.meilcli.net

副作用のあるコードはなぜいけないのか
Go to 副作用のあるコードはなぜいけないのか

テストコードを書かないについては、テストを書かないといけないと言ってるわけではなく、テストが必要になればテストが書ける設計のほうがいい、怪しい処理はテストを書いてほうがいいといった感じです。中にはテストによる統治を好む人がいるかもしれませんが、あくまで自分は「自分が書いたコードが信用できないからテストを書く」派です。

使わなくなったViewの処理は残しておくについては、挙げること自体悩んだ事柄です。個人的には、汎用性のある処理は残しておくほうがいい、汎用性のある実装をしたほうがいいと考えています。ここで伝えたいのは、処理の最終責務者であるView2の処理はほとんどの場合で汎用性がなく、残しておいても無駄ということです。ひょっとしたら世間ではgit管理していないプロジェクトがあるかもしれませんが、git管理していれば過去の実装は引っ張ってこれますし、何の参考にもならないViewの旧実装は残しておく必要もなく、むしろコード量が膨大になり可読性・改修性を悪くします。

共通部分は基盤クラスを用意して共通化するについては補足が必要なところです。処理の共通化自体が悪と主張しているわけではありません。共通化の際に副作用のあるコードにしてしまいやすいので注意が必要といった感じです。
特に、protectedフィールドで派生クラスにフィールドを公開するのは注意が必要です。言い換えてしまえば、これはフィールドをpublicで公開するのと同じような効能があります。基盤クラスの処理の中で使われているフィールドを不用意に派生クラスでnull代入してしまった、初期化してしまったというコードを生みかねないです。
可読性に影響を与える観点から考えると、基盤クラスに処理をまとめすぎると基盤クラスがfat化してしまいます。適切に基盤クラスを分割、拡張メソッドで表現するなどの注意が必要になってきます。

可読性が悪くなる

  • できる限り1つのファイルにまとめる

特に述べるほどでもないかもしれないですが、1つのファイルにまとめすぎると、特定の処理がどこでされているかファイル内で見当がつきにくく可読性を悪化させます。 適切なファイル分割を心がけましょう。

未来の自分や後続の開発者が設計の意図を把握できない/把握するのに時間がかかる

  • レイヤーの責務が定まっていない
  • ソースコードの配置箇所は定まっていない
  • 他のアーキテクチャーを自称する
  • 名前付けはそのときの気分次第

これらに関係しますが、未来の自分や後続の開発者には設計の意図を把握してもらう必要があることを忘れている/考えていないプロジェクトのことを指しています。

最初に作った本人は何らかの基準があるのかもしれない(そもそも基準はないのだろうけど)ですが、未来の自分や後続の開発者はそれらの基準を把握する必要があります。把握できなければ、新たに改修・追加の必要があるコードをどこに書けばいいのか迷ってしまいます。迷わずにてきとーに書けばいいと思う方もいられるかもしれませんが、そうするとソースコードがスパゲッティになっていき、最終的には可読性・改修性へ影響していきます。

他のアーキテクチャーを自称するについてですが、よく見かけるのはMVVMを自称しているにもかかわらずMVVMの基本的な要素からかけ離れ、しかもそのことはREADMEに書いていないというプロジェクトです。最終的に言いたいことはアーキテクチャーのことをREADMEなどで適切に説明せよ、ということなのですが、特にMVVMについては自称するプロジェクトが多すぎて困ったものです。(一時期MVVMが.NET以外でも流行ったからなのだろうか)
MVVMを自称したプロジェクトにMVVMを経験したことのある開発者が入ると、MVVMに沿った設計をされていると考えてしまいますが、MVVMからかけ離れれば離れるほどギャップが生まれ、そのギャップをコードを読んでインプットしないといけなくなります。MVVMやFluxやMVIなどは各レイヤーの責務などが細かく決まっていたりするけども個々のプロジェクトで多少設計が変動するものですので、変動した分は適切に説明を加えていきましょう。

MVVMについては過去に最小限となる要素を書いているので参考にどうぞ↓

MVVMとはなんぞやを公理から求めてみる

MVVM(Model-View-ViewModel)はC#/.NETの世界で生まれたアーキテクチャーですが、今では他の世界(Androidとか)でも利用されています。しかしながら、C#/.NETの世界から他の世界へ輸出される際に間違った解釈で移されていたり、言語・フレームワーク上の性質から妥協をしすぎて本来のMVVMとは言えないものまでMVVMと呼ばれていることもあります。

blog.meilcli.net

MVVMとはなんぞやを公理から求めてみる
Go to MVVMとはなんぞやを公理から求めてみる

名前付けはそのときの気分次第に関連しますが、プログラムにおいて名前というのは重要です。**ほんとに。**名前はてきとーに付けるものではありませんが、気分次第で命名規則を変えることだけはやめましょう。(後続の人がすごく悩むことになる)
特にinterfaceにI prefixをつけるのとつけないのが混ざっているプロジェクトをたびたび見ますが、明確な基準が提示されていないと、どういったときにIを付けるのか悩むことになるので非常に邪悪です。

理想のアーキテクチャーを目指して

アーキテクチャーは多々あり、特徴も多々ありといった感じに正解がない類のものですが、不正解というのは導き出せるものと思っています。
不正解を正せばそれは正解により近づくので、不正解をあぶりだし、直していきましょう。

幸いにも不正解というのはここでキメラアーキテクチャーと呼ばれているものなので、このキメラアーキテクチャーを反面教師的にして理想のアーキテクチャーへ近づけていきましょう!!

※結構殴り書きになってしまったので、ここで挙げた要素以外にもキメラ要素が世の中にはあるのかもしれないですね

Footnotes

  1. 個人的、または属している小コミュニティの間での俗称

  2. アプリケーションではViewが最終的な責務者となることが多い