web-dev-qa-db-ja.com

データレイヤー関数を呼び出すすべてのときにビジネスレイヤーが必要ですか

ASP.Net Webフォームコードを継承しました。ここで、ビジネスレイヤーはデータアクセスレイヤーの関数を呼び出しているだけです。例えば

public string GetCountry(int countryCode)
{
  MyDLSrv obj = new MyDLSrv();
  return obj.GetCountry(countryCode);
}

つまり、DALとBLで関数のメソッドシグネチャが見つかれば、多かれ少なかれ同じです。これは作り上げの例であり、完璧ではありません。つまり、チェック/検証/試行キャッチです。しかし、問題は、このBLがDL以外にどのような値を追加しているのかということです。プレゼンテーション層で直接アクセスできませんか?それは良い方法ですか?そうでない場合、正しい方法は何ですか?

4
Milind Thakkar

データレイヤー関数を呼び出すすべての場合、ビジネスレイヤーは必要ですか?

ビジネスレイヤーを読み取ることにより、データストアへの書き込み時に使用されるオブジェクト(つまり、検証などを含むオブジェクト)を取得し、これらのオブジェクトを変換してユーザーに出力する場合、答えは単純ですいいえ

数年前、システム構築への新しいアプローチが CQRS(コマンドクエリの責任分離) と呼ばれるGreg Youngによって公開されました。

簡単に言えば、CQRS(コマンド-書き込み、クエリ-読み取り)は、データストアと対話するときに少なくとも2つ(またはそれ以上)のオブジェクトを作成するアプローチに過ぎません。1つは書き込み用、もう1つは読み取り用です。データストアに書き込む場合、発行されたコマンドが有効であり、システムの状態に反しないという複雑な検証があります。一方、すべてのコマンドが厳密な検証を通過する場合は、システムに信頼でき、常に有効なデータが含まれているため、クエリ(読み取り)は単純なDTOを使用してデータストアからデータを取り出し、ユーザーに直接出力できます。 (ビジネスレイヤーとビューの間でデータを変換する手間なし)。

イベントソーシングと組み合わせると、CQRSは、書き込みが行われているデータストアとはまったく異なるデータストアにクエリを実行することにより、読み取りレイヤーを別のレベルに引き上げます。書き込みは NF 正規化されたデータベースに対して行われ、書き込みのたびにエンティティの非正規化(キャッシュ)バージョンが非正規化データストアに発行されます。次に、クエリは非正規化データストアにクエリを実行し、パフォーマンスを向上させます。これを考慮すると、クエリで使用されるデータストアが完全に異なるため、クエリ(読み取り)がビジネスレイヤーをまったく通過できないことは明らかです。

データのクエリのみを実行している場合は、ビジネスレイヤーを回避しても問題ありません。

4
Andy

ビジネス層は必ずしも必要ではありません。しかし、はい、アプリケーションを大規模に開発している場合は、レイヤー、パターン、実装などの非常に具体的な範囲である必要があります。それ以降、実際にページ固有であるかどうかを区別し、あらゆる観点から明確なビューを得ることができますコード、UIの設計、UIの統合、ワークフローOR他のいくつかのレイヤーOR DBの統合。これは、実際の開発段階にいるときに非常に役立ちます。

4
Ashraf.Shk786

あなたはあなたの質問に対するコメントで言った:

DALコードは、使用される一般的なADO.Net関数に似ています。つまり、SQL接続を開き、コマンドを定義し、データアダプタに入力して、DataSetを取得します。 ほとんどのDALは、DataSetまたはDataTableを返します。したがって、再書き込みが必要な場合は、破棄して再度書き込みます

(強調、私の)

あなたが最初に尋ねるべき質問は:

それらのDataSetおよびDataTableオブジェクトで何が行われていますか?

これはあなたのビジネスロジック層です!

「ビジネスロジックレイヤー」が単に「データアクセスコード」を呼び出している場合は、ビジネスロジックレイヤーはありません。まったく何もしないレイヤーがもう1つあります。

これは、ビジネスロジックレイヤーが必要ないという意味ではありません。 DataSetおよびDataTableオブジェクトを使用するコードがビジネスロジックを実行することを意味します。元の投稿で説明しているのは、単なるデータアクセスロジックです。


正確には、DAL関数がデータセットまたはデータテーブルを返す場合、BLはそれを返します。 DALがboolまたはintを返す場合、BLは同じを返します。つまり、メソッドシグネチャが見つかれば、多かれ少なかれ同じです。

これは「ビジネス層」とは何かについての誤解を示していると思います。あなたが持っているものは、その名前にもかかわらず、まったくビジネス層ではありません。残念ながら、これはASP.NET WebFormsアプリケーションで頻繁に見られます。

  1. はい、「ビジネス層」が必要です。

  2. あなたの質問にあるのは「ビジネス層」ではなく、あなたはそれを必要としません。

  3. DataSetとDataTableを使用するコード(おそらくフォームとコントロールのコードビハインド)は、オブジェクト指向言語を利用していない巨大な混乱した手続き型コードでのコントローラーの動作とプレゼンテーションの動作が混在する実際のビジネスレイヤーです。書かれています。

    チェーンソーでチーズをスライスするようなものです。またはウッドチッパーで紙吹雪を作る。

ここでisデータアクセスとアプリケーションの残りの部分との間のこの追加のレイヤーを価値ある設計パターンにしますが、実際のクラスとオブジェクトを優先して、無意味でタイプセーフでないDataSetを放棄する必要があります。 : リポジトリパターン

ブログを例として使用します。これは、a)シンプルなコンセプトであり、b)実際に使用しているエンティティがわからないためです。

まず最初に。 DataSetsとDataTablesを忘れます。 「ブログ」というエンティティクラスを作成して、オブジェクト指向言語の機能を使い始めましょう。

public class Blog
{
    /// <summary>
    /// Initializes a new Blog object that has not been saved yet
    /// </summary>
    /// <param name="name"></param>
    public Blog(string name)
    {
        if (string.IsNullOrEmpty(name))
            throw new ArgumentNullException("name");

        Name = name;
    }

    /// <summary>
    /// Initializes a new Blog object that has been previously saved
    /// </summary>
    /// <param name="id"></param>
    /// <param name="name"></param>
    public Blog(int id, string name)
        : this(name)
    {
        Id = id;
    }

    public int Id { get; private set; }
    public string Name { get; private set; }
}

ここでは、ブログにはIDと名前の2つが必要なことを除いて、ビジネスロジックはそれほど多くありません。

「BL」または「ビジネス層」と呼んでいるのは、実際にはBlogクラスでなければなりません。あなたが今持っているものをリポジトリに変えることができます。テストを容易にするために、通常はリポジトリのインターフェースを宣言することをお勧めします。

public interface IBlogRepository
{
    Blog Find(int id);
}

Entity FrameworkやNHibernateのようなORMを使用していない場合、リポジトリにはゲートウェイとファクトリの2つの要素が必要になります。これは、さらに2つのインターフェイスに対応します。これにより、変更があった場合に正確な永続化メカニズム(SQL ServerからOracle、またはWebサービスやフラットファイルストレージ)を簡単に交換できます。さらに、後でアプリケーションのさまざまなレイヤーにキャッシングを実装できます。

ゲートウェイ:

/// <summary>
/// Represents a gateway object that holds the query logic for blogs
/// </summary>
public interface IBlogGateway
{
    DataRow Find(int id);
}

ここではDataRowを返すことに注意してください。 IBlogRepositoryインターフェースをもう一度見て、それがしない汎用データオブジェクトを返し、代わりに厳密に型指定されたBlogオブジェクトを返すことに注意してください。ゲートウェイはデータベースのクエリを担当します。これは、現在の[〜#〜] dal [〜#〜]に類似しています。

次は、DataRowオブジェクトを取得して実際のBlogオブジェクトを作成する「ファクトリー」です。これは、Data Mapperでもあります(別の話題のWordが必要な場合(これは、必要に応じてさらに別のクラスになる可能性があります)。

public interface IBlogFactory
{
    Blog Create(DataRow row);
}

これでインターフェースが整ったので、これらのインターフェースを実装するいくつかの具象クラスを見てみましょう。

これは「BL」と呼ぶ中間層であり、存在の必要性に疑問を投げかけているため、「リポジトリ」から始めます。この設計パターンには目的があり、それはゲートウェイとファクトリーの間で調整することです。

public class BlogRepository : IBlogRepository
{
    public BlogRepository(IBlogGateway gateway = null, IBlogFactory factory = null)
    {
        this.gateway = gateway ?? new SqlServerBlogGateway();
        this.factory = factory ?? new SqlServerBlogFactory();
    }

    private IBlogGateway gateway;
    private IBlogFactory factory;

    public Blog Find(int id)
    {
        DataRow row = gateway.Find(id);

        if (row == null)
            return null;

        return factory.Create(row);
    }
}

注意すべき3つのこと:

  1. コンストラクターはIBlogGatewayおよびIBlogFactoryオブジェクトを受け取ります。デフォルトはnullです。

  2. Nullの場合、Sql Serverの実装にフォールバックします(後で詳しく説明します)。

  3. FindメソッドはIDを受け取り、それをゲートウェイに渡します。ゲートウェイはDataRowを返し、それがBlogを返すファクトリに渡されます。

次に、現在の[〜#〜] dal [〜#〜]がこの部分に適合するように見えるため、ゲートウェイを探索します。

public class SqlServerBlogGateway : IBlogGateway
{
    private const string SQL_FIND_BY_ID = "SELECT * FROM [dbo.Blog] WHERE id = @Id";

    private string ConnectionString
    {
        get
        {
            return System.Configuration.ConfigurationManager.ConnectionStrings["MyApplication"].ConnectionString;
        }
    }

    public DataRow Find(int id)
    {
        using (var connection = new SqlConnection(ConnectionString))
        using (var command = new SqlCommand(SQL_FIND_BY_ID, connection))
        {
            var adaptor = new SqlDataAdaptor(command);
            var data = new DataSet();

            command.Parameters.Add(new SqlParameter("Id", id));
            connection.Open();
            adaptor.Fill(data);

            if (data.Tables.Count == 0 || Data.Tables[0].Rows.Count == 0)
                return null;

            return data.Tables[0].Rows[0];
        }
    }
}

SqlServerBlogGatewayクラスでは、FindメソッドがSQL Serverクライアントライブラリと直接対話して、データベースに対してクエリを実行します。

パズルの最後のピースはファクトリーです。ファクトリーは汎用データオブジェクトを受け取り、厳密に型指定されたBlogオブジェクトを返します。

public class SqlServerBlogFactory : IBlogFactory
{
    public Blog Create(DataRow row)
    {
        int id = Convert.ToInt32(row["ID"]);
        string name = row["NAME"].ToString();

        return new Blog(id, name);
    }
}

呼び出すBlogクラスのコンストラクター、データベース内の列の名前、およびそれらをBlogオブジェクトにマップする方法を認識しています。

そしてもちろん、リポジトリを使用した架空のコード:

IBlogRepository blogs = new BlogRepository(); // Defaults to SQL Server

// Now we have a real object to use, instead of a meaningless,
// generic data structure with no compile time checks:
Blog blog = blogs.Find(123);

キャッシングのレイヤーを導入することもできます:

public CachedBlogGateway : IBlogGateway
{
    public CachedBlogGateway(IBlogGateway innerGateway)
    {
        this.innerGateway = innerGateway;
        this.cache = new Dictionary<int, DataRow>();
    }

    private IBlogGateway innerGateway;
    private Dictionary<int, DataRow> cache;

    public DataRow Find(int id)
    {
        if (cache.ContainsKey(id))
            return cache[id];

        DataRow row = innerGateway.Find(id);

        if (row != null)
            cache[id] = row;

        return row;
    }
}

次に、このインメモリキャッシュを使用します。

IBlogRepository blogs = new BlogRepository(
    new CachedBlogGateway(new SqlServerBlogGateway()));

// Hit the database for this one:
Blog blog1 = blogs.Find(123);

// Hit the cache for this one:
Blog blog2 = blogs.Find(123);

これは長い答えでしたが、ここで「BL」が必要かどうか、およびこの中間層couldを使用して別の層以外の何かを行う方法である主な問題に対処したいと思いますアプリケーションの。

3
Greg Burghardt