n 层架构:存储业务对象的最佳位置?

发布于 2024-08-13 18:10:49 字数 263 浏览 3 评论 0原文

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

现在我正在开发一个项目,其中我的一些数据访问对象具有 Save() 等需要访问数据项目的方法。因此,如果我尝试在数据项目中使用模型/公共项目,我将产生循环引用。

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

Say I have a 3-tier architecture (UI, Business, and Data). Usually, I create a 4th project called "Model" or "Common" to keep my data access objects and each of the other projects would then use this project.

Now I'm working on a project where some of my data access objects have methods like Save() etc that need access to the Data project. So, I would have a circular reference if I attempted to use the Model/Common project in the Data project.

In this scenario, where is the best place to keep the data access objects? I could keep it within the Data project itself, but then my UI project which needs to know about the data access objects, would need to access the Data layer, which is not good.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

李不 2024-08-20 18:10:49

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

在真正的 3 层项目中,仅允许数据层与数据库通信。您可以在“模型”或“通用”项目中使用它。这些项目您的数据层。但您的不同之处在于,只应允许业务层与他们交谈。根本不应该允许您的演示代码与数据层项目进行交互。

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

I don't think you have your n-tier quite right. It sounds like you're building more 2-tier systems.

In a real 3-tier project, only your data tier is allowed to talk to the database. You have that with your "Model" or "Common" projects. Those projects are your data tier. But where you veer off is that only the business tier should be allowed to talk to them. Your presentation code should not be allowed to talk to the data tier projects at all.

n-Tier comes in when you have more than 3 "tiers", but the same principle appliers: each tier only knows how to use (and only needs a reference to) the one below it, and then provides an api for the tier above it. In my own projects, I take your typical presentation, business, and data tiers and provide a 4th "translation" tier between business and data. This way the data tier can return generic types like dataset, datatable, and datarow, and the business tier only has to work in terms of strongly-typed business objects. The translation tier only converts between the generic data objects and strongly-typed objects. This way a change to one of the traditional tiers is less likely to require a change in another.

绮筵 2024-08-20 18:10:49

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

1.) Application.Infrastruct

  • 所有业务对象的基类、业务对象集合、数据访问类以及我的自定义属性和实用程序作为扩展方法、通用验证框架。这决定了我的最终 .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.Infrastruct 除外]

我的所有查询都只定义了 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)
    {
    }
}

This is what I have in my project.

1.) Application.Infrastructure

  • Base classes for all businessobjects, busines object collection, data-access classes and my custom attributes and utilities as extension methods, Generic validation framework. This determines overall behavior organization of my final .net application.

2.) Application.DataModel

  • Typed Dataset for the Database.
  • TableAdapters extended to incorporate Transactions and other features I may need.

3.) Application.DataAccess

  • Data access classes.
  • Actual place where Database actions are queried using underlying Typed Dataset.

4.) Application.DomainObjects

  • Business objects and Business object collections.
  • Enums.

5.) Application.BusinessLayer

  • Provides manager classes accessible from Presentation layer.
  • HttpHandlers.
  • My own Page base class.
  • More things go here..

6.) Application.WebClient or Application.WindowsClient

  • My presentation layer
  • Takes references from Application.BusinessLayer and Application.BusinessObjects.

Application.BusinessObjects are used across the application and they travel across all layers whenever neeeded [except Application.DataModel and Application.Infrastructure]

All my queries are defined only Application.DataModel.

Application.DataAccess returns or takes Business objects as part of any data-access operation. Business objects are created with the help of reflection attributes. Each business object is marked with an attribute mapping to target table in database and properties within the business object are marked with attributes mapping to target coloumn in respective data-base table.

My validation framework lets me validate each field with the help of designated ValidationAttribute.

My framrwork heavily uses Attributes to automate most of the tedious tasks like mapping and validation. I can also new feature as new aspect in the framework.

A sample business object would look like this in my application.

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)
    {
    }
}
温暖的光 2024-08-20 18:10:49

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

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

总之:

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

希望这会有所帮助。

The Data layer should store information in terms of rows and columns (maybe using typed DataSets, if you like), if you are using a relational backend. No "business objects".

The Business layer should use your "business objects". It can have a reference to the BusinessObjects project.

In summary:

  • UI has references to Business and BusinessObjects
  • Business has references to BusinessObjects and Data

Hope this helps.

遗弃M 2024-08-20 18:10:49

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

I have a BusinessObjects project, server side storing the mappings (ORM) and a corresponding DataAccess service exposing CRUD operations on them (and others also like GetAll) etc.

北恋 2024-08-20 18:10:49

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

I would suggest creating and interface of what you want in the model project, and implementing that definition in the data layer. That way all three (four?) projects can use that definition, without knowing how it's implemented.

紫﹏色ふ单纯 2024-08-20 18:10:49

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

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

In my opinion, only the business layer should have knowledge of the data access objects. It should use them for data operations while applying its own business rules and logic, then return dumb objects (e.g. data transfer objects) to the UI layer above.

You could use some thing like AutoMapper to automatically map between you data and business objects.

爱已欠费 2024-08-20 18:10:49

它实际上取决于模式,如果您使用 MVC(前端控制器模式),则模型是应用程序操作所依据的数据的特定于域的表示(通常是 ORM 对此提供帮助),我们对此类使用 DATA 项目。

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

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

It really depend on the pattern, if you are using MVC (Front Controller Pattern), the model Is the domain-specific representation of the data upon which the application operates (generally an ORM help with this) we use a DATA project for this classes.

Models are not data access objects, so the data access becomes in form of repositories in a Different project. Services for Business Rules and finally the Web project. In this approach the Data.dll is referenced in all projects.
The Model is like omnipresent.

DATA(Domain Model) -> REPOSITORY(Data Access) -> SERVICE(Business Rules) -> WEB
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文