ここ数年、私たちは少しずつ一歩ずつ、より良い記述コードに徐々に切り替えてきました。私たちはついに、少なくともSOLIDに似たものへの切り替えを始めていますが、まだまだそこにはありません。切り替えを行って以来、開発者からの最大の不満の1つは、以前はすべてのタスクで開発者が5〜10個のファイルを操作するだけで十分だった数十個のファイルをピアレビューおよびトラバースできないことです。
切り替えを開始する前に、アーキテクチャは次のように編成されていました(1〜2桁以上のファイルが付与されています)。
Solution
- Business
-- AccountLogic
-- DocumentLogic
-- UsersLogic
- Entities (Database entities)
- Models (Domain Models)
- Repositories
-- AccountRepo
-- DocumentRepo
-- UserRepo
- ViewModels
-- AccountViewModel
-- DocumentViewModel
-- UserViewModel
- UI
ファイルに関しては、すべてが非常に線形でコンパクトでした。明らかにコードの重複、密結合、および頭痛の種がたくさんありましたが、誰もがそれを横断して理解することができました。完全な初心者、つまりVisual Studioを開いたことのない人は、わずか数週間でそれを理解できました。全体的なファイルの複雑さの欠如により、初心者の開発者や新規採用者も、あまり時間をかけずに貢献を始めることが比較的簡単になります。しかし、これはコードスタイルの利点が出てこないところです。
コードベースを改善するためのあらゆる試みを心から支持しますが、このような大規模なパラダイムシフトについて、チームの他のメンバーからプッシュバックを得るのは非常に一般的です。現在の最大のこだわりのポイントは次のとおりです。
- 単体テスト
- クラス数
- ピアレビューの複雑さ
ユニットテストは時間の無駄だと信じており、個々のコードよりも全体としてコードをはるかに迅速に処理テストできると信じているため、チームにとっては信じられないほど難しい販売でした。SOLIDの承認として単体テストを使用することはほとんど無益であり、この時点ではほとんど冗談になりました。
クラス数はおそらく克服すべき最大のハードルです。5〜10個のファイルを使用していたタスクは、70〜100個かかるようになりました。これらの各ファイルにはそれぞれ異なる目的がありますが、膨大な量のファイルが圧倒される場合があります。チームからの反応は、主にうめき声と頭を掻くものでした。以前は、タスクには1つまたは2つのリポジトリ、1つまたは2つのモデル、ロジックレイヤー、およびコントローラーメソッドが必要でした。
単純なファイル保存アプリケーションを構築するには、ファイルが既に存在するかどうかを確認するクラス、メタデータを書き込むクラスDateTime.Now
、ユニットテストの時間を注入できるように抽象化するクラス、ロジックを含むすべてのファイルのインターフェイス、ファイルがあります各クラスの単体テストと、DIコンテナにすべてを追加するための1つ以上のファイルを格納します。
中小規模のアプリケーションの場合、SOLIDは非常に簡単に販売できます。誰もが保守性の利点と容易さを理解しています。ただし、非常に大規模なアプリケーションでは、SOLIDの価値提案が見られないだけです。だから私は、組織と管理を改善して、成長する痛みを乗り越える方法を見つけようとしています。
最近完了したタスクに基づいて、ファイルボリュームの例を少し強くしたいと思いました。ファイル同期要求を受信するために、新しいマイクロサービスのいずれかに機能を実装するタスクが与えられました。要求が受信されると、サービスは一連の検索とチェックを実行し、最終的にドキュメントをネットワークドライブと2つの個別のデータベーステーブルに保存します。
ドキュメントをネットワークドライブに保存するには、いくつかの特定のクラスが必要でした。
- IBasePathProvider
-- string GetBasePath() // returns the network path to store files
-- string GetPatientFolderName() // gets the name of the folder where patient files are stored
- BasePathProvider // provides an implementation of IBasePathProvider
- BasePathProviderTests // ensures we're getting what we expect
- IUniqueFilenameProvider
-- string GetFilename(string path, string fileType);
- UniqueFilenameProvider // performs some filesystem lookups to get a unique filename
- UniqueFilenameProviderTests
- INewGuidProvider // allows me to inject guids to simulate collisions during unit tests
-- Guid NewGuid()
- NewGuidProvider
- NewGuidProviderTests
- IFileExtensionCombiner // requests may come in a variety of ways, need to ensure extensions are properly appended.
- FileExtensionCombiner
- FileExtensionCombinerTests
- IPatientFileWriter
-- Task SaveFileAsync(string path, byte[] file, string fileType)
-- Task SaveFileAsync(FilePushRequest request)
- PatientFileWriter
- PatientFileWriterTests
したがって、かなり簡単な保存を実行するために、合計で15のクラス(POCOとscaffoldingを除く)になります。この数は、いくつかのシステムのエンティティを表すためにPOCOを作成し、他のORMと互換性のないサードパーティシステムと通信するためにいくつかのリポジトリを作成し、特定の操作の複雑さを処理するロジックメソッドを作成する必要があるときに大きく膨れ上がりました。