12

GUI、ビジネス ロジック、データ アクセスの 3 層に分割されたアプリケーションがあるとします。ビジネス ロジック レイヤーでは、ビジネス オブジェクト (ゲッター、セッター、アクセサーなど) を記述しました。ビジネス ロジック層へのインターフェイスは、ビジネス ロジックの安全な使用を保証するため、呼び出すすべてのメソッドとアクセサーが入力を検証します。

これは、信頼できる適切に定義されたインターフェイスを持っているため、最初に UI コードを作成する場合に最適です。

しかし、ここで注意が必要なことがあります。データ アクセス レイヤーを書き始めると、ビジネス ロジックへのインターフェイスがニーズに対応できなくなります。非表示になっている/使用されているフィールドを設定するには、より多くのアクセサーとゲッターが必要です。ここで、ビジネス ロジックのインターフェイスを侵食する必要があります。UIレイヤーにはビジネス設定がないUIレイヤーからフィールドを設定できるようになりました。

データ アクセス レイヤーに必要な変更が原因で、ビジネス ロジックへのインターフェイスが侵食され、ビジネス ロジックに無効なデータを設定することさえ可能になりました。したがって、インターフェースはもはや安全な使用を保証しません。

問題を十分に明確に説明したことを願っています。インターフェイスの侵食を防ぎ、情報の隠蔽とカプセル化を維持しながら、異なるレイヤー間で異なるインターフェイスのニーズに対応するにはどうすればよいでしょうか?

4

9 に答える 9

7

質問を正しく理解していれば、ドメインモデルを作成し、データベース内のレコードとドメインオブジェクトの間をマッピングするオブジェクトリレーショナルマッパーを作成したいと考えています。ただし、オブジェクトのフィールドの読み取りと書き込みに必要な「配管」コードでドメインモデルを汚染することを懸念しています。

一歩後退すると、基本的に、データマッピングコードを配置する場所には、ドメインクラス自体内または外部マッピングクラス内の2つの選択肢があります。最初のオプションは、Active Recordパターンと呼ばれることが多く、各オブジェクトがそれ自体を永続化する方法を知っており、ビジネスに関連しないフィールドを公開することなくマッピングを実行できるように、内部構造に十分にアクセスできるという利点があります。

例えば

public class User
{
    private string name;
    private AccountStatus status;

    private User()
    {
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public AccountStatus Status
    {
        get { return status; }
    }

    public void Activate()
    {
        status = AccountStatus.Active;
    }

    public void Suspend()
    {
        status = AccountStatus.Suspended;
    }

    public static User GetById(int id)
    {
        User fetchedUser = new User();

        // Lots of database and error-checking code
        // omitted for clarity
        // ...

        fetchedUser.name = (string) reader["Name"];
        fetchedUser.status = (int)reader["statusCode"] == 0 ? AccountStatus.Suspended : AccountStatus.Active;

        return fetchedUser;
    }

    public static void Save(User user)
    {
        // Code to save User's internal structure to database
        // ...
    }
}

この例では、NameとAccountStatusを持つユーザーを表すオブジェクトがあります。ステータスが直接設定されることを許可したくありません。おそらく、変更が有効なステータス遷移であることを確認したいため、セッターがありません。幸い、GetByIdメソッドとSave静的メソッドのマッピングコードは、オブジェクトの名前フィールドとステータスフィールドに完全にアクセスできます。

2番目のオプションは、マッピングを担当する2番目のクラスを持つことです。これには、ビジネスロジックと永続性のさまざまな懸念事項を分離できるという利点があり、設計をよりテスト可能で柔軟にすることができます。このメソッドの課題は、名前フィールドとステータスフィールドを外部クラスに公開する方法です。いくつかのオプションは次のとおりです。1。リフレクションを使用する(オブジェクトのプライベート部分を深く掘り下げることに何の問題もありません)2。特別な名前のパブリックセッターを提供し(たとえば、「プライベート」という単語をプレフィックスとして付けます)、誤って使用しないようにします3 。言語がそれをサポートしている場合は、セッターを内部にしますが、データマッパーモジュールへのアクセスを許可します。たとえば、.NET2.0以降のInternalsVisibleToAttributeまたはC++のフレンド関数を使用します。

詳細については、MartinFowlerの古典的な本「PatternsofEnterpriseArchitecture」をお勧めします。

ただし、警告として、独自のマッパーを作成する前に、nHibernateやMicrosoftのEntity Frameworkなどのサードパーティのオブジェクトリレーショナルマッパー(ORM)ツールの使用を検討することを強くお勧めします。私は4つの異なるプロジェクトに取り組んできました。さまざまな理由で、独自のマッパーを作成しました。エンドユーザーに価値を提供するコードを作成する代わりに、マッパーの保守と拡張に多くの時間を浪費するのは非常に簡単です。私はこれまで1つのプロジェクトでnHibernateを使用してきましたが、最初はかなり急な学習曲線がありますが、早い段階で投資したことでかなりの成果が得られます。

于 2008-08-13T06:08:24.597 に答える
5

これは古典的な問題です。ドメイン モデルをデータベース モデルから分離することです。それを攻撃する方法はいくつかありますが、私の意見では、プロジェクトのサイズによって異なります。他の人が言ったように、リポジトリパターンを使用できます。.net または Java を使用している場合は、NHibernateまたはHibernateを使用できます。

私がやっていることは、テスト駆動開発を使用しているため、最初に UI レイヤーとモデル レイヤーを作成し、データ レイヤーをモック化することです。そのため、UI とモデルはドメイン固有のオブジェクトを中心に構築されます。その後、これらのオブジェクトを使用しているテクノロジにマップします。データ層。データベースにアプリのデザインを決定させ、最初にアプリを作成し、後でデータについて考えるようにするのは、非常に悪い考えです。

ps 質問のタイトルは少し誤解を招くものです

于 2008-08-12T21:28:09.823 に答える
1

@アイス^^ヒート:

データ層がビジネス ロジック層を認識してはならないというのはどういう意味ですか? ビジネス オブジェクトにどのようにデータを入力しますか?

UI は、ビジネス層の ServiceClass にサービスを要求します。つまり、必要なパラメーター データを含むオブジェクトによってフィルター処理されたオブジェクトのリストを取得します。
次に、ServiceClass は、データ層にリポジトリ クラスの 1 つのインスタンスを作成し、GetList(ParameterType フィルター) を呼び出します。
次に、データ層がデータベースにアクセスしてデータを取得し、それを「ドメイン」アセンブリで定義された共通形式にマップします。
BL はこのデータを処理する必要がないため、UI に出力します。

次に、UI はアイテム X を編集しようとします。アイテム (またはビジネス オブジェクト) をビジネス層のサービスに送信します。ビジネス層はオブジェクトを検証し、OK であれば、ストレージのためにデータ層に送信します。

UI はビジネス層のサービスを認識しており、ビジネス層もデータ層を認識しています。

UI は、オブジェクトとの間でユーザー データ入力をマッピングする役割を担い、データ層は、データベース内のデータをオブジェクトとの間でマッピングする役割を担います。ビジネス層は純粋にビジネスのままです。:)

于 2008-08-12T21:34:47.127 に答える
0

問題は、ビジネス層がより多くの機能をデータ層に公開する必要があることです。この機能を追加すると、UI 層に多くの機能を公開することになりますか? 私があなたの問題を正しく理解しているとすれば、単一のインターフェースで満足させようとしているように思えます。ビジネス層に 2 つのインターフェイスを持たないのはなぜですか? 1 つは、UI レイヤー用のシンプルで安全なインターフェイスです。もう 1 つは、データ層の下位レベルのインターフェイスです。

この 2 つのインターフェイスのアプローチは、UI レイヤーとデータ レイヤーの両方に渡す必要があるオブジェクトにも適用できます。

public class BusinessLayer : ISimpleBusiness
{}

public class Some3LayerObject : ISimpleSome3LayerObject
{}
于 2008-08-12T21:52:01.570 に答える
0

私は常に、以下を含む別のアセンブリを作成します。

  • 多くの小さなインターフェイス (ICreateRepository、IReadRepository、IReadListRepsitory を考えてみてください。リストは続き、それらのほとんどはジェネリックに大きく依存しています)
  • IPersonRepository のように、IReadRepository から継承する多くの具体的なインターフェイスがあることは理解できます。
    小さなインターフェイスだけでは記述できないものはすべて、具体的なインターフェイスに入れます。
    IPersonRepository を使用してオブジェクトを宣言している限り、操作するクリーンで一貫したインターフェイスが得られます。しかし、キッカーは、コンストラクターで fx ICreateRepository を取るクラスを作成することもできるため、コードは非常にファンキーなことを行うのが非常に簡単になります。ここには、ビジネス層のサービス用のインターフェースもあります。
  • 最後に、すべてのドメイン オブジェクトを追加のアセンブリに貼り付けます。これは、コード ベース自体を少しクリーンにし、より疎結合にするためです。これらのオブジェクトにはロジックはありません。3 つ以上のレイヤーすべてのデータを記述する一般的な方法です。

ところで。データ層に対応するためにビジネス ロジック層でメソッドを定義するのはなぜですか?
データ層には、ビジネス層があることを知る必要さえありません..

于 2008-08-12T21:16:50.987 に答える
0

インターフェイスを侵食しないため、解決策になる可能性があります。私はあなたがこのようなクラスを持つことができると思います:

public class BusinessObjectRecord : BusinessObject
{
}
于 2008-08-12T21:18:44.650 に答える
0

データ層がビジネス ロジック層を認識してはならないということは、どういう意味ですか? ビジネス オブジェクトにどのようにデータを入力しますか?

私はよくこれを行います:

namespace Data
{
    public class BusinessObjectDataManager
    {
         public void SaveObject(BusinessObject object)
         {
                // Exec stored procedure
         {
    }
}
于 2008-08-12T21:28:05.753 に答える
0

インターフェースを次の 2 つのタイプに分割したい場合があります。

  • ビュー インターフェース -- これは、UI との対話を指定するインターフェースです。
  • データ インターフェース -- これは、データとの相互作用を指定できるようにするインターフェースです。

次のようなインターフェイスの両方のセットを継承して実装することができます。

public class BusinessObject : IView, IData

このように、データ レイヤーでは IData のインターフェイス実装のみを確認する必要があり、UI では IView のインターフェイス実装のみを確認する必要があります。

使用したい別の戦略は、UI レイヤーまたはデータ レイヤーでオブジェクトを構成して、これらのレイヤーによって単に消費されるようにすることです。

public class BusinessObject : DomainObject

public class ViewManager<T> where T : DomainObject

public class DataManager<T> where T : DomainObject

これにより、ビジネス オブジェクトが UI/View レイヤーとデータ レイヤーの両方を認識しないままにすることができます。

于 2008-08-13T01:00:12.783 に答える
0

私はこれまでの慣習に逆らい続け、なぜこれらの恐ろしく複雑なオブジェクト レイヤーをすべて構築しているのかを疑問視する必要があると言いたいと思います。

多くの開発者は、データベースをオブジェクトの単純な永続レイヤーと考えており、それらのオブジェクトが必要とする CRUD 操作のみに関心があると思います。オブジェクト モデルとリレーショナル モデルの間の「インピーダンスの不一致」に多大な労力が費やされています。ここにアイデアがあります:試すのをやめてください。

データをカプセル化するストアド プロシージャを記述します。コードから必要に応じて、結果セット、DataSet、DataTable、SqlCommand (または java/php/同等のもの) を使用して、データベースと対話します。それらのオブジェクトは必要ありません。良い例は、SqlDataSource を .ASPX ページに埋め込むことです。

データを誰にも隠そうとしないでください。開発者は、物理データ ストアと対話する方法とタイミングを正確に理解する必要があります。

オブジェクト リレーショナル マッパーは悪魔です。それらの使用を停止します。

エンタープライズ アプリケーションの構築は、多くの場合、複雑さを管理するための作業です。物事をできるだけシンプルに保つ必要があります。そうしないと、システムを完全に保守できなくなります。いくつかの結合を許可する場合 (これはどのアプリケーションにも固有のものです)、ビジネス ロジック レイヤーとデータ アクセス レイヤーの両方をなくすことができ (それらをストアド プロシージャに置き換えます)、それらのいずれも必要ありません。インターフェイス。

于 2008-08-13T22:45:03.680 に答える