Business Logic Layer and Data Access layer: circul

2019-01-12 23:48发布

问题:

I’m having a little Architecture problem. In my project I have a Business Logic Layer (BLL) that contains all my business rules, models and OO API for the interface. Each object has static methods like getById that return an instance of said object. Each object also has methods like save and, delete. This is very straightforward OO code.

Now I have a DataAccess layer (DAL), contained in a separate namespace, for each BLL object I have a DataClass or “Repository” which executes the getById and save commands. So in a way, the BLL save and getById methods are a thin layer around the DataClass methods.

public static NewsItem GetByID(int id)
{
       return DataFactory.GetNewsItemRepository().GetNewsItemById(id);
}

In order for the DataClasses to return BLL objects, they need to know the BLL. so now we have:

GUI ---> BLL <---->DAL

The DataFactory only returns objects that implement an Interface, so I can hide implementation details like “OracleNewsItemRepository”.

But now for the thing that has been bugging me ever since I started Object Oriented programming. In my current solution, both BLL and the DAL need to know each other. This is a Circular Dependency, and it is best practice to avoid circular dependencies. Also I only want to expose the interfaces (and my DataFactory) and not my classes. This can be done by placing the DAL layer in a separate Assembly. Which would make sense. However, Visual Studio does not allow two Assemblies to refer eachother. Another question about this: C# internal access modifiers

Somehow I think I got my whole data access pattern wrong. It feels like I am convoluting the ActiveRecord pattern with other stuff like DataMappers. I have spent a lot of time on Martin Fowler’s site, but those patterns are described very generic and are illustrated by a very abstract UML diagram.

They don’t solve my problem. Maybe I’m a bit anal, and there is no such thing as a “perfect data access pattern”. And what I do now doesn’t seem terribly wrong. But how I do things now, seems off…

Any ideas?

回答1:

I think your data access pattern is fine. What you are not doing is coupling your BLL to the OracleDAL. You are coupling to the DAL interfaces. A certain bit of coupling is absolutely required or you could never get anything done.

I assume that your DataFactory and the INewsItemRepository classes exist outside your DAL Layer. The following is an example of how my solutions are organized. I don't use ActiveRecord, so this may not suit you perfectly.

Core (Project)
  Domain
    Business Entities
  Data
    Repository Interfaces
    **Your DataFactory**

OracleData (Project)
  Data
    Oracle Repository Implementations

SqlData (Project)
  Data
    Sql Repository Implementations

UI (Project)

Hope this helps.



回答2:

In my opinion:

The Data Access Layer (DAL) should operate on POCOs (Plain old CLR objects) using operations such as: SaveNewsItem ( NewsItemDAO newsItemDAO ). The POCOs are your DAOs (Data Access Objects).

The Business Layer should contain the logic to convert a Data Access Object (DAO) into a rich business object, which is probably just the DAO plus some operations as well as any decoration/enrichment.

The DAL should have no knowledge at all about the Business Logic Layer. It should, in theory, be able to be called from any client. For example, what if you wanted to seperate the DAL from the application and deploy it as a seperate service exposing itself via WCF?

As mentioned, the DAL operations, e.g. SaveNewsItem should be be accessed by the BO via interfaces, perhaps via dependency injection/IoC.



回答3:

You can use interfaces/dependency injection to solve your problem.

Your business layer (BL) contains the data access (DA) interfaces that the (possibly more than one) DAL need to implement. The DAL projects have project references to BL so that they can spit out business objects (BOs) and implement the DA interfaces.

Your BOs can call DataFactory, which can instantiate a DA object via dependency injection or reflection.

I have used this pattern in many of our applications here at work (both web-based and smart-client), and it works beautifully.



回答4:

It's a bit old now but perhaps you should have considered putting the pocos/interfaces in another assembly.

Project.Data references Project.Entities
Project.BL references Project.Entities and Project.Data
Project.UI references Project.Entities and Project.BL

Here there are no circular references.



回答5:

Just to be clear, I like to think of a Business Model and Business Logic as two separate layers. Your business model are your POCOs (Plain old CLR objects). Your business logic layer would be responsible for performing validations, transactions, etc using both your business model and an interface to your DAL that could be wired up a number of ways (Spring, Castle, or your own home grown IoC container).

A good way to achieve zero dependencies in your DAL with your business model is to use an already built object relation mapping framework (ORM) such as NHibernate (a shameless plug for my favorite ORM framework).



回答6:

IceHeat, @Craig Wilson's example makes the most sense and its probably derived from this article: http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx.

This is WELL worth a read and tackles domain driven development which resolves the issues you are facing here. Id recommend it to anyone, even if you don't give a monkeys about NHibernate its a great article.



回答7:

I would remove any Get() and Save() method from you BLL (Domain model).. here is what I would do

GUI will ask Repository to get domain object by id.. and once GUI has the domain object you could navigate it to reach other objects.. This way Domain layer does not need to know anything about repositories..

Inside repository you could return domain object that lazily load or completely load the object object graph.. this would depend on you need..

Here is a good write up on the same topic...Reconstituting objects

Read the comment by Deyan Petrov on how to use dynamic proxy



回答8:

DAL must be abstract, so it must contains only plain ADO.NET objects that interact with backend database e.g connection DataAdapter, DataReader and so on. With that at hand you can reference DAL in you Biz layer, and when it comes to your entities with a little abstraction you can solve all your problems, for example if you have customer class, you can make an abstaractoin customer class that implement the the basic operations to interact with DAL like save, update and retrieve data and in another class that inherits the abstraction class override the base class methods implementation to meet your Biz validation and so on.