【问题标题】:n-tier architecture: best place to store business objects?n 层架构:存储业务对象的最佳位置?
【发布时间】:2010-12-24 15:34:10
【问题描述】:

假设我有一个 3 层架构(UI、业务和数据)。通常,我会创建一个名为“Model”或“Common”的第四个项目来保存我的数据访问对象,然后其他每个项目都会使用这个项目。

现在我正在处理一个项目,其中我的一些数据访问对象具有需要访问数据项目的 Save() 等方法。因此,如果我尝试在 Data 项目中使用 Model/Common 项目,我会有一个循环引用。

在这种情况下,保存数据访问对象的最佳位置在哪里?我可以将它保存在 Data 项目本身中,但是我的 UI 项目需要了解数据访问对象,需要访问 Data 层,这不好。

【问题讨论】:

  • 我应该澄清一下,我所说的业务对象实际上是数据访问对象——这些类的属性与我的数据库表中的字段相关。
  • 小心混淆术语“层”和“层”。听起来您在谈论的是层,而不是层。

标签: c# .net asp.net 3-tier n-tier-architecture


【解决方案1】:

我认为您的 n 层不太正确。听起来您正在构建更多的 2 层系统。

在真正的 3 层项目中,只允许您的数据层与数据库通信。你的“模型”或“通用”项目就有了。这些项目您的数据层。但是你偏离的地方是只有业务层应该被允许与他们交谈。根本不应该允许您的演示代码与数据层项目对话。

n-Tier 在你有超过 3 个“层”时出现,但同样的原则适用:每一层只知道如何使用(并且只需要引用)它下面的一个,然后提供一个 api 用于它上面的层。在我自己的项目中,我采用典型的演示、业务和数据层,并在业务和数据之间提供第四个“转换”层。这样,数据层可以返回数据集、数据表和数据行等通用类型,而业务层必须根据强类型业务对象工作。翻译层在通用数据对象和强类型对象之间进行转换。这样,对传统层级之一的更改不太可能需要对另一个层级进行更改。

【讨论】:

  • 好点,但您需要在第二段的最后一句中添加“不”。
  • 实际上我的模型从不与数据库对话。只是现在我在一个我接手的项目中,其中自定义对象具有数据访问方法——因此我的问题。顺便说一句,你为什么需要翻译层?您不能让您的业务层直接使用通用对象吗?
【解决方案2】:

这就是我的项目中的内容。

1.) Application.Infrastructure

  • 所有业务对象的基类、业务对象集合、数据访问类以及我的自定义属性和实用程序作为扩展方法,通用验证框架。这决定了我最终的 .net 应用程序的整体行为组织。

2.) Application.DataModel

  • 数据库的类型化数据集。
  • TableAdapters 已扩展以包含我可能需要的事务和其他功能。

3.) Application.DataAccess

  • 数据访问类。
  • 使用底层类型化数据集查询数据库操作的实际位置。

4.) Application.DomainObjects

  • 业务对象和业务对象集合。
  • 枚举。

5.) Application.BusinessLayer

  • 提供可从表示层访问的管理器类。
  • HttpHandlers。
  • 我自己的 Page 基类。
  • 这里有更多内容..

6.) Application.WebClientApplication.WindowsClient

  • 我的表示层
  • 从 Application.BusinessLayer 和 Application.BusinessObjects 获取引用。

Application.BusinessObjects 在整个应用程序中使用,并在需要时跨越所有层 [Application.DataModel 和 Application.Infrastructure 除外]

我所有的查询都只定义了 Application.DataModel。

Application.DataAccess 作为任何数据访问操作的一部分返回或获取业务对象。业务对象是在反射属性的帮助下创建的。每个业务对象都标有到数据库中目标表的属性映射,业务对象中的属性标有与相应数据库表中目标列的属性映射。

我的验证框架允许我在指定的 ValidationAttribute 的帮助下验证每个字段。

我的框架大量使用属性来自动化大多数繁琐的任务,例如映射和验证。我还可以将新功能作为框架中的新方面。

我的应用程序中的示例业务对象如下所示。

User.cs

[TableMapping("Users")]
public class User : EntityBase
{
    #region Constructor(s)
    public AppUser()
    {
        BookCollection = new BookCollection();
    }
    #endregion

    #region Properties

    #region Default Properties - Direct Field Mapping using DataFieldMappingAttribute

    private System.Int32 _UserId;

    private System.String _FirstName;
    private System.String _LastName;
    private System.String _UserName;
    private System.Boolean _IsActive;

    [DataFieldMapping("UserID")]
    [DataObjectFieldAttribute(true, true, false)]
    [NotNullOrEmpty(Message = "UserID From Users Table Is Required.")]
    public override int Id
    {
        get
        {
            return _UserId;
        }
        set
        {
            _UserId = value;
        }
    }

    [DataFieldMapping("UserName")]
    [Searchable]
    [NotNullOrEmpty(Message = "Username Is Required.")]
    public string UserName
    {
        get
        {
            return _UserName;
        }
        set
        {
            _UserName = value;
        }
    }

    [DataFieldMapping("FirstName")]
    [Searchable]
    public string FirstName
    {
        get
        {
            return _FirstName;
        }
        set
        {
            _FirstName = value;
        }
    }

    [DataFieldMapping("LastName")]
    [Searchable]
    public string LastName
    {
        get
        {
            return _LastName;
        }
        set
        {
            _LastName = value;
        }
    }

    [DataFieldMapping("IsActive")]
    public bool IsActive
    {
        get
        {
            return _IsActive;
        }
        set
        {
            _IsActive = value;
        }
    }

    #region One-To-Many Mappings
    public BookCollection Books { get; set; }

    #endregion

    #region Derived Properties
    public string FullName { get { return this.FirstName + " " + this.LastName; } }

    #endregion

    #endregion

    public override bool Validate()
    {
        bool baseValid = base.Validate();
        bool localValid = Books.Validate();
        return baseValid && localValid;
    }
}

BookCollection.cs

/// <summary>
/// The BookCollection class is designed to work with lists of instances of Book.
/// </summary>
public class BookCollection : EntityCollectionBase<Book>
{
    /// <summary>
    /// Initializes a new instance of the BookCollection class.
    /// </summary>
    public BookCollection()
    {
    }

    /// <summary>
    /// Initializes a new instance of the BookCollection class.
    /// </summary>
    public BookCollection (IList<Book> initialList)
        : base(initialList)
    {
    }
}

【讨论】:

    【解决方案3】:

    如果您使用的是关系型后端,数据层应该以行和列的形式存储信息(如果您愿意,可以使用类型化的数据集)。没有“业务对象”。

    业务层应该使用您的“业务对象”。它可以引用 BusinessObjects 项目。

    总结:

    • UI 引用了 Business 和 BusinessObjects
    • Business 引用了 BusinessObjects 和 Data

    希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      我有一个 BusinessObjects 项目,服务器端存储映射 (ORM) 和相应的 DataAccess 服务,在它们上公开 CRUD 操作(以及其他也如 GetAll)等。

      【讨论】:

        【解决方案5】:

        我建议在模型项目中创建和接口您想要的内容,并在数据层中实现该定义。这样所有三个(四个?)项目都可以使用该定义,而不知道它是如何实现的。

        【讨论】:

          【解决方案6】:

          在我看来,只有业务层应该了解数据访问对象。它应该在应用自己的业务规则和逻辑的同时将它们用于数据操作,然后将哑对象(例如数据传输对象)返回到上面的 UI 层。

          您可以使用 AutoMapper 之类的东西在您的数据和业务对象之间自动映射。

          【讨论】:

            【解决方案7】:

            这真的取决于模式,如果您使用 MVC(前端控制器模式),模型是应用程序操作的数据的特定域表示(通常是 ORM 帮助)我们使用 DATA 项目对于这个类。

            模型不是数据访问对象,因此数据访问以不同项目中的存储库的形式出现。业务规则服务,最后是 Web 项目。在这种方法中,Data.dll 在所有项目中都被引用。 模型无所不在。

            DATA(Domain Model) -> REPOSITORY(Data Access) -> SERVICE(Business Rules) -> WEB
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2023-03-27
              • 2013-01-22
              • 2011-04-26
              • 2011-06-02
              • 2010-09-12
              • 2017-07-05
              • 2013-08-06
              • 2023-03-25
              相关资源
              最近更新 更多