Harza
Harza

Reputation: 111

How to pass the object type from business layer to data layer

I'm creating n-tier application architecture. Different layers does not known anything of each others internal implementations and communication between layers is handled trough very narrow interfaces assisted with IoC/DI.

Now I'm passing business objects (defined in business layer) to data layer. Business layer itself does not known nothing how and where data is actually saved (usually database but business layer should not know that). Business objects are passed to data layer trough implementation of IDataReader (this approach also supports bulk loading of data in future scenarios). Data layer reads all data from IDataReader and calls some stored procedure to save data to database (if one object is saved, IDataReader returns "one row").

Actual problem is here:

Because I just pass IDataReader to data layer and disconnecting data from its type, what is best way in this case to determine place of data?

Example if I pass actual business object of type "User" to data layer, then data layer should save data into table "User". In some other case data might be saved to some different structure independently of the type in business layer.

In current implementation I pass type information of business object into data layer and data layer inspect this type and desides where to place data.

Is it right solution to data layer inspect incoming data and desides its place or should data layer expose list of "places" where data can be saved (enum?)?

Thanks in advance!

/Clarification:

Options that I see in here are: 1. Data layer gives list of "plces" where data can be saved 2. Data layer inspect given arguments (like that type arg) and desides where to store data

First option penalty; What if I try to store business object of type "Product" into structure used usually by type "User"

Second option penalty; I have to map type "Namespace1.Namespace2.User" to specific routine that saves its data to table "User". So manually do some mapping for EACH type...

/Clarification 2:

Now I do retrieve like this:

service.Retrieve(typeof(Catalog), null, catalogArgs, e => catalogConverter.Read(e));

So I pass typeof(Catalog) to data layer... so I have type info in data layer.

Now in data layer I need to select "Adapter" that does hard work to get data from database. I can write enormous if/switch structure to select the adapter ... which is frustrating. Also I can write attribute and use it like this:

[TypeAdapterAttribute("Namespace1.Namespace2.Catalog, and assembly info...")]
class CatalogAdapter { ... }

Then I have code that does mapping from given type to attribute value...

...but it is little bit bloat and problematic with that hard coded type string...

Any suggestions...?

/Clarification 3

I have idea that this system can be extended by "pluggable business modules". These modules contains business objects (and some logic) that are not know by base-business-logic, also Data layer is fully unaware of these business objects contained in "plugged assemblies". This external business module has no reference to base-business-layer or data layer. When business object from this external assembly is saved (=sent to data layer) data layer saves it by default to EAV style (http://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model) data structure automatically. Because this kind of data structure might have some performance problems, data layer must have way to deside to save specific business object types in their own dedicated data structure (usually table with one-to-one mapping to that type). So problem comes here, how to implement this "selection-decision" :)

Idea here is that I can create lot of new business objects and I can save all of them to data layer without actually code anything to data layer! Later time I can create own dedicated data structures for selected business objects If needed.

Upvotes: 3

Views: 4406

Answers (2)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112279

If you want to create your own database layer that is disconnected from the business layer, you could also introduce a separate assembly with contracts

List<Customer> customers = DB.LoadCustomers();

The data layer can work with generic type parameters and get information about the business object via Reflection. This provides a good separation of the layers because the data layer needs no reference to business components.

List<Customer> customers = DataContext.Query<Customer>.Load();

or

Customer customer = DataContext.Query<Customer>.Load(custID);

O/R-mappers typically work like this

List<Customer> customers = Context.Query<Customer>()
    .Where(cust => cust.Name.StartsWith("A"))
    .OrderBy(cust => cust.Name)
    .ToList();

In these examples, the data layer creates and fills the business objects. Otherwise, the business layer has to know the table column names.

(Note: I am not referring to specific data interfaces here.)


UPDATE:

If you want to create your own database layer that is diconnected from the business layer you could also introduce a separate assembly with contracts

public interface ICustomer
{
    string LastName { get; set; }
    string FirstName { get; set; }
    ...
}

Both, the data layer and the business layer would have a reference to these contracts.

In the data layer, you would have a method similar to

public List<T> LoadAll<T>(Func<T> create)
{
    var list = new List<T>();
    if (T is ICustomer) {
        string sql = "SELECT * FROM tblCustomer";
        ...
        while (reader.NextResult()) {
            ICustomer cust = (ICustomer)create();
            cust.FirstName = reader.GetString("FirstName");
            ...
            list.Add((T)cust);
        }
    } else if (T is IOrder) {
        ...
    }
    return list;
}

In the business layer you would write

List<ICustomer> customers = DbLayer.LoadAll<ICustomer>(() => new Customer());

Now, your data layer can work with customers without knowing your customer class and without having a reference to the business layer assembly.

Upvotes: 1

David
David

Reputation: 3804

I'd recommend you use a ORM (Entitiy Framework, NHibernate) or a micro-ORM (PetaPoco, Dapper) to map your objects to data stores.
Look into that and shoot if you got a specific question.

UPDATE: I think I just got what you're asking.
You need to define a new method in your data layer for each type. So:

public User GetUserById(int id);
public void SaveUser(User user);
public Product GetProductById(int id);
public void SaveProduct(Product product);

Upvotes: 0

Related Questions