Devlimeの再利用設計で保守性を高める

Devlimeの再利用設計で保守性を高める

1. 背景

Devlimeの開発は、複数の開発者がそれぞれの機能とドメインを担当し、同時に開発を進める方式で行われていました。Vizendプラットフォームを基盤として開発されているため、プロジェクト構造や基本的な開発方式はある程度統一されており、全体的なアーキテクチャも十分に整備されていました。

しかし、製品規模が大きくなり機能が増加するにつれて、既存機能を修正したり、他の開発者が作成したコードを分析したりする状況が徐々に増えていきました。その過程で、同一または非常によく似た機能であるにもかかわらず、開発者ごとに異なる方法で実装されているケースを頻繁に見つけるようになりました。

例えば、特定のデータを取得するロジックは複数の画面で同じように使用されていましたが、各サービスで個別に実装されていました。また、画面上で状態を表示するUIも、似た機能であるにもかかわらず、互いに異なるスタイルと構造が使用されていました。

初期の開発段階では、このような方式は大きな問題にはなりませんでした。しかし、機能が増加するにつれて重複コードが急速に増え、同じ機能を修正するために複数の箇所を探して変更しなければならない状況が繰り返し発生しました。

また、画面ごとに表現方法が異なるため、ユーザーが感じるUI/UX体験にも一貫性が欠けるという問題がありました。

そこで、単に機能の実装に集中するのではなく、繰り返し使用されるロジックとUIを共通化して再利用性を高め、保守コストを削減する方向で改善を進めました。

2. 既存方式の問題点

2.1 バックエンド

バックエンドでは、同じ機能であるにもかかわらず開発者ごとに実装方式が異なり、次のような問題が発生していました。

第一に、可読性が低下していました。同じ機能を実行するコードが異なる形式で実装されていたため、コードを読む側が機能を理解するのに多くの時間を要していました。

第二に、再利用性が不足していました。特定のライブラリAPI呼び出しや共通データ取得ロジックが、複数のサービスに重複して実装されていました。例えば、従業員情報を取得するロジックが複数のサービスに直接記述されていました。

Member member = memberClient.findMember(citizenId);

このような方式では、新しい機能を開発するたびに同じコードを繰り返し記述することになりました。

第三に、保守コストが増加していました。同じ機能が複数の箇所に存在するため、修正が必要になった場合は、すべての場所を探して変更しなければなりませんでした。

第四に、エラーが発生する可能性が高まりました。似たロジックであっても、一部のサービスでは例外処理や検証ロジックが欠落するケースがあり、同じ機能であるにもかかわらず異なる結果が返される問題が発生する可能性がありました。

2.2 フロントエンド

フロントエンドにも同様の問題が存在していました。

第一に、同じ情報を表現する画面が異なる方式で動作していました。例えば、Status、Priority、Typeのようなデータは、画面ごとにそれぞれ異なる方法で表示されていました。

第二に、UI/UXに一貫性がありませんでした。同じ情報を表示するコンポーネントであっても、画面ごとに色、スタイル、レイアウトが異なる形で適用されていました。

第三に、繰り返し使用される多言語処理ロジックが存在していました。MemberオブジェクトのDisplay Nameを表示するたびに、各画面で個別の言語処理ロジックを記述していました。

第四に、共通機能が再利用されていませんでした。メンバープロフィール、時間表現、エラー処理、パターン処理などの機能が、複数の画面で重複して実装されていました。

3. 改善方針と設計

問題を解決するため、まず繰り返し使用される機能を分析しました。この過程で、共通化の対象は大きくバックエンドとフロントエンドに分けることができました。

3.1 バックエンドの共通化対象

バックエンドでは、次の項目を優先的に選定しました。

  • Enumクラス

  • ライブラリクライアントの活用ロジック

  • 共通取得ロジック

  • 繰り返し使用されるドメイン関数

  • 共通Taskロジック

3.2 フロントエンドの共通化対象

フロントエンドでは、次の項目を中心に共通化を進めました。

  • Enumの表現方式

  • 状態および優先度の表示UI

  • メンバープロフィールUI

  • 多言語処理関数

  • Timezone処理関数

  • Grid共通設定

  • Error処理

単に関数を共通化するだけでなく、ユーザー体験の一貫性を維持する方向で設計を進めました。

4. 適用過程

4.1 バックエンド共通モジュールの分離

まず、繰り返し使用されるライブラリ呼び出しロジックを共通化しました。従来は各サービスが直接ライブラリを呼び出していました。以下は簡単なサンプルコードです。

Member member = memberClient.findMember(citizenId);

これをExtern層として分離しました。

@Service
@RequiredArgsConstructor
public class MemberExtern {

    private final MemberClient memberClient;

    public Member getMember(String citizenId) {
        return memberClient.findMember(citizenId);
    }
}

これにより、ライブラリ依存性を一か所で管理できるようになり、例外処理や後処理ロジックも併せて共通化できるようになりました。

4.2 Enumの表現方式の改善

従来は、Enumの値そのものが画面に表示されていました。

Late
LeftEarly
HolidayWork

しかし、ユーザーにとって分かりやすい表現が難しく、UIを変更するとドメインにも影響が及ぶという問題がありました。これを解決するため、ドメイン値とUI表現値を分離しました。

LeftEarly("Early Leave")

また、フロントエンドではDisplayName Mapを通じて画面上の表現を管理するように改善しました。これにより、画面の変更が必要になっても、ドメインモデルを修正せずに対応できるようになりました。

4.3 多言語処理の共通化

MemberオブジェクトのDisplay Nameを画面ごとに個別処理していた方式を改善しました。従来は各画面で言語を直接判定していました。

改善後は、共通関数を通じて処理する方式に変更しました。

getMemberDisplayName(member)

これにより、多言語処理ロジックを一か所で管理できるようになり、言語ポリシーの変更時に修正範囲を最小限に抑えられるようになりました。

4.4 UIコンポーネントの共通化

繰り返し使用されるメンバープロフィールUIを共通コンポーネントとして分離しました。

従来は画面ごとにAvatarの生成、イニシャルの計算、Tooltipの構成をそれぞれ実装していました。改善後は、1つのコンポーネントのみを使用する方式に変更しました。以下のように、特定のUI/UX要素をコンポーネントとしてまとめ、パラメータを通じてコンポーネント内の自由度を高めました。

<MemberProfileGroup
  members={members}
  maxVisible={10}
/>

これにより、画面デザインの統一性を確保でき、新しい画面でも同じユーザー体験を提供できるようになりました。

5. 適用結果

再利用可能なコード設計を適用した後、次のような効果を確認できました。

第一に、開発生産性が向上しました。従来は新しい機能を開発するたびに似たようなロジックを再実装する必要がありましたが、共通モジュールとコンポーネントを活用することで、開発時間を大幅に短縮できました。

第二に、保守性が向上しました。共通ロジックが一か所に集約されることで、修正時に影響を受ける範囲を容易に把握できるようになり、重複した修正作業も減少しました。

第三に、コード品質が向上しました。同じ機能に対して1つの実装方式を使用することで、例外処理および検証ロジックを一貫して適用できるようになりました。

第四に、UI/UXの一貫性が向上しました。状態表示、メンバー情報、多言語処理などの共通機能を同じ方式で提供することで、ユーザー体験の統一性を確保できました。

最後に、共通化の過程で整理した内容をNotionのドキュメントとして共有し、チームメンバーが同じ基準で開発できるようにしました。これにより、単なるコードの再利用にとどまらず、チーム全体の開発生産性と保守性を向上させる基盤を整えることができました。

Bignow

Site footer