Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

implementing Database-functionality objects in an N-Tier architecture?

I'm adding functionality to our website which performs long-running processes asynchronously using MSMQ. Doing this ansynch, however means we need to notify users when their requests are completed. Using the command pattern, I created an interface* called INotify and composed that into the message class, so the message processing class can simply call GiveNotice() on the message's INotify object. The first implementation, EmailNotify, was more difficult than expected, as I was surprised to discover MailMessage isn't serializable, but got it going.

Now I'm working on a new concrete notifier, DBNotify, which will call a SP of some sort and update a status in the main transactional database. I'm tripped up in that I would like to reuse the DAL architecture we've already created, but INotify is a member of the Model project, which is more fundamental than the DAL.

Our hierarchy looks like this: Common > Model > DAL > BAL

Here's more detail about the tiers. Bear in mind, I inherited this from : Common is responsible for all "utility" functions which are used many places in the application, things like accessing configuration settings, parsing strings, non-business related functionality.

Model are business objects, what some folks call data transfer objects, collections of getters and setters. I've added some "smarts" at this layer, but only business rules internal to that object, such as "An item's name must begin with an alphanumeric character."

DAL is the data access layer, in theory, all that happens here is model objects are moved into and out of the database.

BAL is the Business layer; in theory, business rules that govern the interaction of objects are enforced (i.e. "A form must have at least two items.").

So the INotify interface is defined an abstraction to allow the method of notification to vary independently (i.e. email, TXT, twitter, etc). It's fundamental to the system, so I have created it at the Model tier, which is independent of the DAL tier. However, I am creating a new concrete implementation of INotify whose notification method is to call a SP in a database.

Has anyone else dealt with a business object whose purpose is to interact with a database, and how do you situate that in your N-tier architecture?

Before you tell me to use Linq to Sql, great thanks. This is not a technical question (how do I do this), it's a design question (how should I do this).

I think there is a StackExchange site more focused on these sorts of language-independant design questions, so I'm going to copy this there.

like image 447
Michael Blackburn Avatar asked Mar 30 '11 16:03

Michael Blackburn


1 Answers

Maybe not really an answer to your question, but something to think about nonetheless.

I am at odds with where you put the data access in your component hierarchy. I would not put it between two functional domain layers. Not even "above" the single domain model classes. Data access, or persistency, is not a concern for any domain class. It should only be something that can be done to them, not something they do.

Even though I started out coding things like TClient.Save and TClient.Load I have now come to the conclusion that it is not the Client that decides it needs to be saved, but user interactions that dictate when a domain instance's data is needed and therefore should be loaded, and when an Client's data should be persisted, if at all. I am therefore now a proponent of coding (in the GUI, more specifically the controller's in the GUI) things like DataStore.Load(ClientInstance) and DataStore.Save(ClientInstance). It is then up to the Data Access layer to figure out how to do that. It could use reflection in C#, or the new RTTI in Delphi to iterate over all the Client's properties so it can send them to a database.

While layering is a very good concept to separate concerns and keep you from putting stuff all over the place by simply adhering to "you can call down but not up", it does not help as much when addressing things like logging, exception handling, notifications and all those other interesting cross cutting concerns that every other component/layer needs.

In addition, the Common layer, as it is a utility layer should really be accessible to all other layers.

To put it all in a picture (where I have kept the distinction you make between simple domain classes, your model, and cross class business rules, your BAL):

+---+   +-------------+
| C |<--| Data Access |<--------------------------+
| o |   +-------------+                           |
| m |         |                                   |
| m |         |                                   |
| o |         v                                   |
| n |   +-------------+   +----------------+   +-----+
|   |<--| Model       +<--| Cross class    |<--| GUI |
|   |   +-------------+   | business rules |   |     |
|   |                     |                |   |     |
|   |<--------------------|                |   |     |
|   |                     +----------------+   |     |
|   |                                          |     |
|   |<-----------------------------------------|     |
+---+                                          +-----+

The INotify implementation that calls into the database is currently in the model, which in the picture above, does not call into the data access layer itself, it is only called, or rather interrogated, by the data access layer.

The question really is whether INotify should be in the "model", a part of the domain layer, or whether it should be a Common interface and there should be a separate "Notification" layer/component that is accessible from the domain and the GUI. This new component could not only concern itself with notifications, but many other cross cutting concerns, for example logging. It has access to the common (of course) and data access components and to the GUI in at least in some sort of call back fashion.

In the picture below I have tried to visualize this, but I am not much good at visualization and always have problems with those pesky cross cutters. Thats why there are no call-arrows from the domain layer to the cross cutting concerns, though of course the domain layer should be able to access for example a "Logger" interface. Maybe I am trying to hard to distinguish between the common and the cross cutting components and an argument could be made to put these together, and visualize them as separate blocks within a "Utility" layer/component.

        +--------------------------------------------+
  +-----| Cross cutting concerns                     |
  |     +--------------------------------------------+
  v           v^                                    ^
+---+   +-------------+                             |
| C |<--| Data Access |<--------------------------+ |
| o |   +-------------+                           | |
| m |         |                                   | |
| m |         |                                   | |
| o |         v                                   | v
| n |   +-------------+   +----------------+   +-----+
|   |<--| Model       +<--| Cross class    |<--| GUI |
|   |   +-------------+   | business rules |   |     |
|   |                     |                |   |     |
|   |<--------------------|                |   |     |
|   |                     +----------------+   |     |
|   |                                          |     |
|   |<-----------------------------------------|     |
+---+                                          +-----+
like image 95
Marjan Venema Avatar answered Nov 01 '22 17:11

Marjan Venema