这是 n 层架构的正确实现吗? [英] Is this a proper implementation of n-layer architecture?

查看:20
本文介绍了这是 n 层架构的正确实现吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在过去一年左右的时间里一直在学习 C#,并试图在此过程中融入最佳实践.在 StackOverflow 和其他网络资源之间,我认为我在正确分离我的关注点的正确轨道上,但现在我有一些疑问,并想确保在我将整个网站转换为这个新网站之前我走的是正确的道路架构.

I have been learning C# for the last year or so and trying to incorporate best practices along the way. Between StackOverflow and other web resources, I thought I was on the right track to properly separating my concerns, but now I am having some doubts and want to make sure I am going down the right path before I convert my entire website over to this new architecture.

当前的网站是旧的 ASP VBscript 并且有一个非常丑陋的现有数据库(没有外键等)所以至少对于 .NET 中的第一个版本我不想使用并且必须在以下位置学习任何 ORM 工具这次.

The current website is old ASP VBscript and has a existing database that is pretty ugly (no foreign keys and such) so at least for the first version in .NET I do not want to use and have to learn any ORM tools at this time.

我有以下项目在单独的命名空间和设置中,因此 UI 层只能看到 DTO 和业务层,而数据层只能从业务层看到.这是一个简单的例子:

I have the following items that are in separate namespaces and setup so that the UI layer can only see the DTOs and Business layers, and the Data layer can only be seen from the Business layer. Here is a simple example:

productDTO.cs

productDTO.cs

public class ProductDTO
{
    public int ProductId { get; set; }
    public string Name { get; set; }

    public ProductDTO()
    {
        ProductId = 0;
        Name = String.Empty;
    }
}

productBLL.cs

productBLL.cs

public class ProductBLL
{

    public ProductDTO GetProductByProductId(int productId)
    {
        //validate the input            
        return ProductDAL.GetProductByProductId(productId);
    }

    public List<ProductDTO> GetAllProducts()
    {
        return ProductDAL.GetAllProducts();
    }

    public void Save(ProductDTO dto)
    {
        ProductDAL.Save(dto);
    }

    public bool IsValidProductId(int productId)
    {
        //domain validation stuff here
    }
}

productDAL.cs

productDAL.cs

public class ProductDAL
{
    //have some basic methods here to convert sqldatareaders to dtos


    public static ProductDTO GetProductByProductId(int productId)
    {
        ProductDTO dto = new ProductDTO();
        //db logic here using common functions 
        return dto;
    }

    public static List<ProductDTO> GetAllProducts()
    {
        List<ProductDTO> dtoList = new List<ProductDTO>();
        //db logic here using common functions 
        return dtoList;
    }

    public static void Save(ProductDTO dto)
    {
        //save stuff here
    }

}

在我的用户界面中,我会做这样的事情:

In my UI, I would do something like this:

ProductBLL productBll = new ProductBLL();
List<ProductDTO> productList = productBll.GetAllProducts();

保存:

ProductDTO dto = new ProductDTO();
dto.ProductId = 5;
dto.Name = "New product name";
productBll.Save(dto);

我完全偏离基地了吗?我的 BLL 中是否也应该有相同的属性,而不是将 DTO 传回我的 UI?请告诉我什么是错的,什么是对的.请记住,我还不是专家.

Am I completely off base? Should I also have the same properties in my BLL and not pass back DTOs to my UI? Please tell me what is wrong and what is right. Keep in mind I am not a expert yet.

我想为我的架构实现接口,但我仍在学习如何做到这一点.

I would like to implement interfaces to my architecture, but I am still learning how to do that.

推荐答案

贫血域是指产品或其他类除了数据设置器和获取器之外没有真正实现任何东西 - 没有域行为.

Anemic domain is when a product or other class doesn't really implement anything more than data setters and getters - no domain behavior.

例如,一个产品域对象应该公开一些方法、一些数据验证、一些真实的业务逻辑.

For instance, a product domain object should have some methods exposed, some data validations, some real business logic.

否则,BLL 版本(域对象)几乎不比 DTO 好.

Otherwise, the BLL version (the domain object) is hardly better than a DTO.

http://martinfowler.com/bliki/AnemicDomainModel.html

ProductBLL productBll = new ProductBLL();
List<ProductDTO> productList = productBll.GetAllProducts();

这里的问题是您预先假设您的模型是贫血的,并将 DTO 暴露给业务层消费者(UI 或其他任何东西).

The problem here is that you are pre-supposing your model is anemic and exposing the DTO to the business layer consumers (the UI or whatever).

您的应用程序代码通常希望与 一起工作,而不是任何 BLL 或 DTO 或其他任何东西.这些是实现类.它们不仅对应用程序程序员的思维水平意义不大,对表面上理解问题域的领域专家也意义不大.因此,如果您明白我的意思,它们应该只在您处理管道时可见,而不是在您设计浴室时可见.

Your application code generally wants to be working with <Product>s, not any BLL or DTO or whatever. Those are implementation classes. They not only mean little to the application programmer level of thought, they mean little to domain experts who ostensibly understand the problem domain. Thus they should only be visible when you are working on the plumbing, not when you are designing the bathroom, if you see what I mean.

我将我的 BLL 对象命名为业务域实体的名称.DTO 位于业务实体和 DAL 之间.当域实体除了 DTO 之外什么都不做时 - 这就是它贫血的时候.

I name my BLL objects the name of the business domain entity. And the DTO is internal between the business entity and the DAL. When the domain entity doesn't do anything more than the DTO - that's when it's anemic.

此外,我还要补充一点,我经常只是省略显式 DTO 类,并将域对象转到具有在配置中定义的有组织的存储过程的通用 DAL,然后将自身从普通的旧数据读取器加载到其属性中.使用闭包,现在可以使用带有回调的非常通用的 DAL,让您插入参数.

Also, I'll add that I often just leave out explcit DTO classes, and have the domain object go to a generic DAL with organized stored procs defined in the config and load itself from a plain old datareader into its properties. With closures, it's now possible to have very generic DALs with callbacks which let you insert your parameters.

我会坚持最简单的方法:

I would stick to the simplest thing that can possibly work:

public class Product {
    // no one can "make" Products
    private Product(IDataRecord dr) {
        // Make this product from the contents of the IDataRecord
    }

    static private List<Product> GetList(string sp, Action<DbCommand> addParameters) {
        List<Product> lp = new List<Product>();
        // DAL.Retrieve yields an iEnumerable<IDataRecord> (optional addParameters callback)
        // public static IEnumerable<IDataRecord> Retrieve(string StoredProcName, Action<DbCommand> addParameters)
        foreach (var dr in DAL.Retrieve(sp, addParameters) ) {
            lp.Add(new Product(dr));
        }
        return lp;
    }

    static public List<Product> AllProducts() {
        return GetList("sp_AllProducts", null) ;
    }

    static public List<Product> AllProductsStartingWith(string str) {
        return GetList("sp_AllProductsStartingWith", cm => cm.Parameters.Add("StartsWith", str)) ;
    }

    static public List<Product> AllProductsOnOrder(Order o) {
        return GetList("sp_AllProductsOnOrder", cm => cm.Parameters.Add("OrderId", o.OrderId)) ;
    }
}

然后您可以将明显的部分移到 DAL 中.DataRecords 用作您的 DTO,但它们的生命周期很短 - 它们的集合从未真正存在.

You can then move the obvious parts out into a DAL. The DataRecords serve as your DTO, but they are very short-lived - a collection of them never really exists.

这是一个静态的 SqlServer 的 DAL.Retrieve(你可以看到它很简单,可以将其更改为使用 CommandText);我有一个封装连接字符串的版本(因此它不是静态方法):

Here's a DAL.Retrieve for SqlServer which is static (you can see it's simple enough to change it to use CommandText); I have a version of this which encapsulates the connection string (and so it's not a static method):

    public static IEnumerable<IDataRecord> SqlRetrieve(string ConnectionString, string StoredProcName,
                                                       Action<SqlCommand> addParameters)
    {
        using (var cn = new SqlConnection(ConnectionString))
        using (var cmd = new SqlCommand(StoredProcName, cn))
        {
            cn.Open();
            cmd.CommandType = CommandType.StoredProcedure;

            if (addParameters != null)
            {
                addParameters(cmd);
            }

            using (var rdr = cmd.ExecuteReader())
            {
                while (rdr.Read())
                    yield return rdr;
            }
        }
    }

稍后您可以转向成熟的框架.

Later you can move on to full blown frameworks.

这篇关于这是 n 层架构的正确实现吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆