.NET Framework - How exactly does interface de-couple the layers in an application?

Asked By csharper on 14-Sep-11 03:18 PM
I really appreciate best practice in the industry and would want to follow suit in my work.  But I have not really understood the relationships between each layer of my application and the interfaces thereof.

For example, suppose I have a 3-tiered application for a university. The 3 tiers are UI, BLL and DAL.  Let's focus on BLL and DAL now.

I know from reading that it is always good practice to program to interface because interfaces decouples the BLL and DAL.  But I do not know exactly how the decoupling between BLL and DAL is achieved through interface programming.

I suppose that in BLL, I will have classes like below:

public class Student
{
public string SSN {get; set; }
public string FirstName {get; set; }
public string LastName {get; set; }
}

And in the DAL (Let's use a Repository), I will have something like below:

public class StudentRepository
{
public Student GetStudentBySSN(string ssn)
{
// snip
}
}

Question 1: Based on best practice, should the BLL.Student class contain a method to call DAL.StudentRepository.GetStudentBySSN(string)?

Question 2: In which layer (BLL or DAL) do I define my interfaces with the goal of decoupling BLL and DAL? Is it gonna be in BLL something like below?

public interface IPerson
{
public Student GetStudentBySSN(string ssn);
}

Any thoughts?  Is there a sample 3-tiered, interaced application I can download? Or do you (some .net architects who haunt this group) have a demo application to share?  Thank you.




Registered User replied to csharper on 14-Sep-11 05:09 PM
You're on the right track. The Student type is part of the model
(model project).

The model also contains an interface; here is a simplified version :

public interface IStudentRepository
{
void Add(Student student);
Student FindBy(string ssn);
IEnumerable<Student> FindAll();
}

Finally there is a service in the model. Again a simplified example :

public class StudentService
{
private IStudentRepository studentRepository;
public StudentService(IStudentRepository studentRepository)
{
this.studentRepository = studentRepository;
}
public IList GetStudentsByName(string firstName, string lastName
{
return (select student from studentRepository.FindAll()
where student.FirstName.Equals(firstName) &&
student.LastName.Equals(lastName)
select student).ToList();
}
}

The service can represent the DAL in your three tier design. The
service can be created as :

IStudentRepository repository = new SomeStudentRepository();
service = new StudentService(repository);

The SomeStudentRepository type implements the IStudentRepository
interface. The actual implementation can use practically any type of
datastore.

Two excellent texts are :
Professional Enterprise .NET
Professional ASP.NET Design Patterns

You can get  the ISBNs and further details from http://www.wrox.com

regards
A.G.
Arne_Vajhøj replied to csharper on 14-Sep-11 06:41 PM
No.

In that case the DAL will have a dependency on the BLL (DAL
class StudentRepository returns a BLL class Student).


It is not good design to have class both contain data
and know how data are to be persisted.


The interface for decoupling BLL and DAL should be in the DAL.

If the BLL only get a ref of type of the interface then the actual
implementation class in DAL can be replaced without affecting BLL.

Arne
Arne_Vajhøj replied to Arne_Vajhøj on 14-Sep-11 06:48 PM
A good decoupled DAL exposes:
* data classes or interfaces (to be very general interfaces are best,
but classes are really OK in my opinion)
* an interface for how to interact with the DAL
* optionally a factory to create DAL (the alternative is to use
reflection or a DI framework to get the desired DAL)

I have a few examples that illustrates the point.

They are no realistic examples, but hopefully they are illustrative.

Arne
Arne_Vajhøj replied to Arne_Vajhøj on 14-Sep-11 06:50 PM
First example:

data class
DAL interface
factory
and two implementations not exposed (internal visibility) for MS Access
and XML

using System;
using System.IO;
using System.Xml;
using System.Data.OleDb;

namespace DAL
{
public class AccountData
{
private string name;
private decimal amount;
public AccountData() : this("", 0.00m)
{
}
public AccountData(string name, decimal amount)
{
this.name = name;
this.amount = amount;
}
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public decimal Amount
{
get
{
return amount;
}
set
{
amount = value;
}
}
}
public interface AccountManager
{
AccountData Load(string name);
void Save(AccountData ac);
}
public class AccountManagerFactory
{
public static AccountManager Create(string amtyp)
{
if(amtyp == "DB")
{
return new DatabaseAccountManager();
}
if(amtyp == "XML")
{
return new XmlAccountManager();
}
else
{
return null;
}
}
}
internal class DatabaseAccountManager : AccountManager
{
public AccountData Load(string name)
{
AccountData res;
OleDbConnection con = new
OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=C:\\Databases\\MSAccess\\Test.mdb");
con.Open();
OleDbCommand sel = new OleDbCommand("SELECT amount FROM
accounts WHERE name='" + name + "'", con);
OleDbDataReader rdr = sel.ExecuteReader();
if(rdr.Read())
{
decimal amount = (decimal)rdr[0];
res = new AccountData(name, amount);
}
else
{
res = null;
}
con.Close();
return res;
}
public void Save(AccountData ac)
{
OleDbConnection con = new
OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=C:\\Databases\\MSAccess\\Test.mdb");
con.Open();
OleDbCommand upd = new OleDbCommand("UPDATE accounts SET
amount = " + ac.Amount + " WHERE name='" + ac.Name + "'", con);
upd.ExecuteNonQuery();
}
}
internal class XmlAccountManager : AccountManager
{
private const string XML_FILE = @"C:\accounts.xml";
private XmlDocument doc;
public XmlAccountManager()
{
doc = new XmlDocument();
doc.Load(XML_FILE);
}
public AccountData Load(string name)
{
XmlNode n =
doc.SelectSingleNode("//accounts/account[@name='" + name +"']");
if(n != null)
{
decimal amount =
decimal.Parse(n.Attributes["amount"].Value);
return new AccountData(name, amount);
}
else
{
return null;
}
}
public void Save(AccountData ac)
{
XmlNode n =
doc.SelectSingleNode("//accounts/account[@name='" + ac.Name +"']");
n.Attributes["amount"].Value = ac.Amount.ToString();
StreamWriter sw = new StreamWriter(XML_FILE);
doc.Save(sw);
sw.Close();
}
}
}

Arne
Arne_Vajhøj replied to Arne_Vajhøj on 14-Sep-11 06:55 PM
Second example:

data class
DAL interface
no factory
and 3 implementations for DataReader, LINQ to EF and NHibernate
(actually public because here they are intended to be loadable
from another assembly)

public class T1
{
public virtual int F1 { get; set; }
public virtual string F2 { get; set; }
}


public interface IDAL
{
T1 GetOneT1(int f1);
IList<T1> GetAllT1();
IList<T1> GetFilterT1(int? f1, string f2);
}


public class DataReaderDAL : IDAL
{
private IDbConnection GetConnection()
{
DbProviderFactory dbf =
DbProviderFactories.GetFactory(ConfigurationManager.ConnectionStrings["Test"].ProviderName);
IDbConnection con = dbf.CreateConnection();
con.ConnectionString =
ConfigurationManager.ConnectionStrings["Test"].ConnectionString;
con.Open();
return con;
}
public T1 GetOneT1(int f1)
{
using(IDbConnection con = GetConnection())
{
string paramprefix =
ConfigurationManager.AppSettings["ParamPrefix"];;
IDbCommand sel = con.CreateCommand();
sel.CommandText = "SELECT f1,f2 FROM t1 WHERE f1 = " +
paramprefix + "f1";
sel.Connection = con;
IDbDataParameter p = sel.CreateParameter();
p.ParameterName = paramprefix + "f1";
p.DbType = DbType.Int32;
p.Value = f1;
sel.Parameters.Add(p);
using(IDataReader rdr = sel.ExecuteReader())
{
if(rdr.Read())
{
return new T1 { F1 = (int)rdr["f1"], F2 =
(string)rdr["f2"]};
}
else
{
return null;
}
}
}
}
public IList<T1> GetAllT1()
{
IList<T1> res = new List<T1>();
using(IDbConnection con = GetConnection())
{
IDbCommand sel = con.CreateCommand();
sel.CommandText = "SELECT f1,f2 FROM t1";
sel.Connection = con;
using(IDataReader rdr = sel.ExecuteReader())
{
while(rdr.Read())
{
res.Add(new T1 { F1 = (int)rdr["f1"], F2 =
(string)rdr["f2"]});
}
}
}
return res;
}
public IList<T1> GetFilterT1(int? f1, string f2)
{
IList<T1> res = new List<T1>();
using(IDbConnection con = GetConnection())
{
string isnull = ConfigurationManager.AppSettings["ISNULL"];
string paramprefix =
ConfigurationManager.AppSettings["ParamPrefix"];;
IDbCommand sel = con.CreateCommand();
sel.CommandText = "SELECT f1,f2 FROM t1 WHERE f1=" +
isnull + "(" + paramprefix + "f1,f1) AND f2 LIKE " + isnull + "(" +
paramprefix + "f2, f2)";
sel.Connection = con;
IDbDataParameter p1 = sel.CreateParameter();
p1.ParameterName = paramprefix + "f1";
p1.DbType = DbType.Int32;
p1.Value = f1 != null ? (object)f1 : (object)DBNull.Value;
sel.Parameters.Add(p1);
IDbDataParameter p2 = sel.CreateParameter();
p2.ParameterName = paramprefix + "f2";
p2.DbType = DbType.String;
p2.Value = f2 != null ? (object)("%" + f2 + "%") :
(object)DBNull.Value;
sel.Parameters.Add(p2);
using(IDataReader rdr = sel.ExecuteReader())
{
while(rdr.Read())
{
res.Add(new T1 { F1 = (int)rdr["f1"], F2 =
(string)rdr["f2"]});
}
}
}
return res;
}
}


public class LINQtoEFDAL : IDAL
{
public T1 GetOneT1(int f1)
{
using(TestContext ctx = new TestContext())
{
return ctx.T1.Where(t1 => t1.F1==f1).First();
}
}
public IList<T1> GetAllT1()
{
using(TestContext ctx = new TestContext())
{
return ctx.T1.ToList();
}
}
public IList<T1> GetFilterT1(int? f1, string f2)
{
using(TestContext ctx = new TestContext())
{
return ctx.T1.Where(t1 => (f1==null || t1.F1==f1) &&
(f2==null || t1.F2.Contains(f2))).ToList();
}
}
}


public class NHibernateDAL : IDAL
{
private ISessionFactory sf;
public NHibernateDAL()
{
Configuration cfg = new Configuration();
cfg.Configure("hibernateconfig.xml");
cfg.AddXmlFile("t1mapping.xml");
sf = cfg.BuildSessionFactory();

}
public T1 GetOneT1(int f1)
{
using(ISession s = sf.OpenSession())
{
return (T1)s.Load(typeof(T1), f1);
}
}
public IList<T1> GetAllT1()
{
using(ISession s = sf.OpenSession())
{
return s.CreateQuery("FROM T1 AS t1").List<T1>();
}
}
public IList<T1> GetFilterT1(int? f1, string f2)
{
using(ISession s = sf.OpenSession())
{
return s.CreateQuery("FROM T1 AS t1 WHERE
t1.F1=COALESCE(:f1,t1.F1) AND t1.F2 LIKE
COALESCE(:f2,t1.F2)").SetParameter("f1", f1,
NHibernateUtil.Int32).SetParameter("f2", "%" + f2 + "%",
NHibernateUtil.String).List<T1>();
}
}
}


IDAL dal = (IDAL)Activator.CreateInstance(Type.GetType(dalimpl, true,
false));

Arne
csharper replied to Arne_Vajhøj on 15-Sep-11 01:30 PM
st"].ProviderName);
+=20
=20
LL"];
=20
Value;
=20
=20
f1) &&=20
=20

Thank you very much. This sample is really helpful. A lot of tutorials onli=
ne keep on talking and talking, but I cannot figure out from them the archit=
ecture from a bird's eye view. So I know from reading that programming to i=
nterface is good, but then I am confused about which layer the interfaces s=
hould stay with.

Question: your class T1 looks like a poco. It should be the BLL, correct?
csharper replied to Registered User on 15-Sep-11 01:41 PM
Arne says that the interface should stay in the DAL. Or maybe we can create a project that only contains interfaces?
csharper replied to Arne_Vajhøj on 15-Sep-11 03:42 PM
I think you have this T1 class inside your DAL since your GetOneT1 method returns a T1.  Otherwise, your DAL will have to refer to BLL to be able to refer to T1. So, again I am confused.

1) Your T1 really looks like a POCO and should be in a BLL, to my understanding.

2) But if 1) is true, then in order to refer to T1 in DAL.GetOneT1(int), DAL will have to reference the BLL, causing the DAL to be dependent on BLL.

3) But if T1 is stuck into the DAL, then why so since to my understanding, it really should be in a BLL?

Again, I do not see how the DAL can be completely independent of the BLL through or not through interfaces.
Big Steel replied to csharper on 15-Sep-11 04:17 PM
You can have an Interface between the UI and Model View Controller or
Model View Presenter design pattern an "IView.cs". You can have an
Interface with a Service layer, BLL and DAL each one with their own
interface and implementations.

A WCF service has its IService.cs.

One writes Unit Test against interfaces.



Unit tests are automated procedures that verify whether an isolated
piece of code behaves as expected in response to a specific input. Unit
tests are usually created by developers and are typically written
against public methods and interfaces. Each unit test should focus on
testing a single aspect of the code under test; therefore, it should
generally not contain any branching logic. In test-driven development
scenarios, developers create unit tests before they code a particular
method. The developer can run the unit tests repeatedly as they add code
to the method. The developer's task is complete when their code passes
all of its unit tests.


Unit Tests can use mocked objects on the interface to run Unit Tests
agaist the objects for expected behavior.



Mock objects have the same interface as the real objects they mimic,
allowing a client object to remain unaware of whether it is using a real
object or a mock object. Many available mock object frameworks allow the
programmer to specify which, and in what order, methods will be invoked
on a mock object and what parameters will be passed to them, as well as
what values will be returned. Thus, the behavior of a complex object
such as a network socket can be mimicked by a mock object, allowing the
programmer to discover whether the object being tested responds
appropriately to the wide variety of states such objects may be in.




You use an interface to define a protocol of behavior that can be
implemented by any class anywhere in the class hierarchy. Interfaces are
useful for the following:

Capturing similarities among unrelated classes without artificially
forcing a class relationship.
Declaring methods that one or more classes are expected to implement.
Revealing an object's programming interface without revealing its class.


You see interfaces used in each one of the layers. Interfaces are
dominant in Web application design using n-tier.
csharper replied to Big Steel on 15-Sep-11 04:27 PM
eate a project that only contains interfaces?
=20
=20
=20
=20
=20
=20
=20
=20

Thank you for your reply, but I think your reply is kinda off-topic. I know=
what interfaces are, but I do not know how to use them between a BLL and DA=
L, in particular, how it helps us decouple BLL and DAL.

And your pasted text of what unit tests/interfaces are and their benefits i=
n terms of software development is totally off-topic. I am afraid my topic i=
s being hijacked. Again, here is my topic: How exactly does interface de-co=
uple BLL and DAL? Any demo/real examples? Arne has nicely shown one of his,=
but I am confused about the whereabout of his T1 class.
Registered User replied to csharper on 15-Sep-11 04:35 PM
The model is part the DAL. At its simplest the model contains a
concrete type representing the data, a concrete service type, and a
repository Interface. The other part of the DAL is implementation of
that repository interface.

The BLL's interaction with the DAL is through the service. The
service's interaction with the datastore implementation is done using
the repository Interface. The BLL knows nothing about the repository
Interface or the actual implementation of the same.

- snip -
regards
A.G.
Big Steel replied to csharper on 15-Sep-11 05:08 PM
What are you talking about here? Are you saying you do not know how to
implement an interface between the BLL and the DAL and how to make the
calls through the interface from calling object to called object? I will
give you an example when I get home if that is what you are talking about.

An interface is just a contract between the calling object and the
object it is calling based on what methods can be called on the called
object.

I cannot say that one needs an interface to decouple the BLL and DAL.
Registered User replied to csharper on 15-Sep-11 05:08 PM
The thread is not being hijacked, you are making the assumption that
decoupling is done between layers rather than within layers. The use
of Interfaces within the DAL decouples the BLL from any specific
implementation of the DAL.

The Wrox text "Professional Enterprise .NET" by Arking and Millett
does a fine business job with both explanations and examples of
domain-driven design and the repository pattern.

regards
A.G.
Big Steel replied to Registered User on 15-Sep-11 05:43 PM
I think if one wanted to decouple the BLL and DAL, then one would
implement a Service layer between the BLL and DAL. The BLL and DAL are
for sure decoupled with the Service layer sitting in between them. :)

And I am not talking about a legacy Web service or WCF Web service or
non Web service.

And yes, I have used BLL and the DAL within the same classlib project
with interface and they were decoupled or "loosely" coupled within the
same classlib project.
Big Steel replied to csharper on 15-Sep-11 07:03 PM
How to use the interface between BLL and DAL objects. There are many
other objects in the DAL that use an interface dedicated to a DAL object
tht it implements. The BLL object calls the methods on the DAL object
based on the interface implementation.


using System.Collections.Generic;
using System.Linq;
using BLL.DTO;
using BLL.IBLL;

namespace BLL
{
public class ArticleManager : IArticleManager
{
public DTOArticle GetArticle(int id)
{
var article = new DataAccessLayer.Article().GetArticle(id);

var dto = new DTOArticle
{
ArticleID = article.ArticleID,
AuthorID = article.AuthorID,
Tille = article.Tille,
Body = article.Body
};
return dto;
}

public List<DTOArticle> GetArticles()
{
var articles = new DataAccessLayer.Article().GetArticles();

var dtos = articles.Select(article => new DTOArticle
{
ArticleID = article.ArticleID,
AuthorID = article.ArticleID,
Tille = article.Tille,
Body = article.Body
}).ToList();

return dtos;
}

public List<DTOArticle> SaveArticles(List<DTOArticle> articles)
{
var artcdtos = new List<DTOArticle>();

var artcupds = (from a in articles.Where(a => a.IsUpdate)
select a).ToList();
var artcadds = (from a in articles.Where(a => a.ArticleID
== -1) select a).ToList();

if (artcupds.Count > 0
|| artcadds.Count > 0)
{
if (artcupds.Count > 0)
{
var dtos = artcupds.Select(artc => new
DataAccessLayer.DTO.DTOArticle
{
ArticleID = artc.ArticleID,
AuthorID = artc.AuthorID,
Tille = artc.Tille,
Body = artc.Body
}).ToList();

new DataAccessLayer.Article().UpdateArticles(dtos);
}

if (artcadds.Count > 0)
{
var dtos = artcadds.Select(artc => new
DataAccessLayer.DTO.DTOArticle
{
ArticleID = artc.ArticleID,
AuthorID = artc.AuthorID,
Tille = artc.Tille,
Body = artc.Body
}).ToList();

new DataAccessLayer.Article().AddArticles(dtos);
}

var returndtos = GetArticles();

artcdtos.AddRange(returndtos.Select(dtoArticle => new
DTOArticle
{
ArticleID = dtoArticle.ArticleID,
AuthorID = dtoArticle.AuthorID,
Tille = dtoArticle.Tille,
Body = dtoArticle.Body,
IsUpdate = dtoArticle.IsUpdate
}));
}
else
{
artcdtos = articles.ToList();
}

return artcdtos;
}

public void DeleteArticle(DTOArticle article)
{
var daldto = new DataAccessLayer.DTO.DTOArticle { ArticleID
= article.ArticleID };
new DataAccessLayer.Article().DeleteArticle(daldto);
}

}
}



DAL Interface

using System.Collections.Generic;
using DataAccessLayer.DTO;

namespace DataAccessLayer.IDAL
{
public interface IArticle
{
DTOArticle GetArticle(int id);
List<DTOArticle> GetArticles();
void UpdateArticles(List<DTOArticle> articles);
void AddArticles(List<DTOArticle> articles);
void DeleteArticle(DTOArticle article);
}
}


DAL Article.cs implements IArticle

using System.Linq;
using DataAccessLayer.IDAL;
using DataAccessLayer.Model;
using DataAccessLayer.DTO;
using System.Data.EntityClient;
using System.Collections.Generic;


namespace DataAccessLayer
{
public class Article :IArticle
{
private const string pcdb = "name=PublishingCompanyEntities";

public DTOArticle GetArticle(int id)
{
var dto = new DTOArticle();

using (var conn = new EntityConnection(pcdb))
using (var db = new PublishingCompanyEntities(conn))
{
try
{
var article = (from a in db.Articles.Where(a =>
a.ArticleID == id)select a).First();

dto.ArticleID = article.ArticleID;
dto.AuthorID = (int) article.AuthorID;
dto.Body = article.Body;
dto.Tille = article.Title;

}
finally
{
conn.Close();
}
}

return dto;
}

public List<DTOArticle> GetArticles()
{
var dtos = new List<DTOArticle>();

using (var conn = new EntityConnection(pcdb))
using (var db = new PublishingCompanyEntities(conn))
{
try
{
var articles = (from a in db.Articles select
a).ToList();

dtos.AddRange(articles.Select(article => new DTOArticle
{
ArticleID = article.ArticleID,
AuthorID = (int) article.AuthorID,
Body = article.Body,
Tille = article.Title
}));
}
finally
{
conn.Close();
}
}

return dtos;
}

public void UpdateArticles(List<DTOArticle> articles)
{
using (var conn = new EntityConnection(pcdb))
using (var db = new PublishingCompanyEntities(conn))
{
try
{
foreach (var article in articles)
{
var artc = new Model.Article { ArticleID =
article.ArticleID };

db.AttachTo("Articles", artc);
artc.AuthorID = article.AuthorID;
artc.Title = article.Tille;
artc.Body = article.Body;
db.SaveChanges();
}
}
finally
{
conn.Close();
}
}
}

public void AddArticles(List<DTOArticle> articles)
{
using (var conn = new EntityConnection(pcdb))
using (var db = new PublishingCompanyEntities(conn))
{
try
{
foreach (var article in articles)
{
var artc = new Model.Article
{
AuthorID = article.AuthorID,
Title = article.Tille,
Body = article.Body
};

db.AddToArticles(artc);
db.SaveChanges();
}
}
finally
{
conn.Close();
}
}
}

public void DeleteArticle(DTOArticle article)
{
var art = new Model.Article { ArticleID = article.ArticleID };

using (var conn = new EntityConnection(pcdb))
using (var db = new PublishingCompanyEntities(conn))
{
try
{
db.AttachTo("Articles", art);
db.DeleteObject(art);
db.SaveChanges();
}
finally
{
conn.Close();
}
}
}
}
}
Arne_Vajhøj replied to csharper on 15-Sep-11 08:40 PM
Given that:
- DAL implementation implements DAL interface
- DAL implementation has to be in DAL
- DAL should not reference BLL
then DAL interface has to be in DAL.


No.

It is part of the exposed DAL API. So it will either have to be
in DAL or by something DAL can reference (and DAL can still not
reference BLL).

Arne
Arne_Vajhøj replied to csharper on 15-Sep-11 08:52 PM
Yes.


Yes.


No.

POCO's can be in any layr.


It is not true.

And T1 will either be in DAL or in something that DAL can reference
(not BLL).

PL->BLL->DAL (with T1)

or:

|->BLL-|
PL-|      |->DAL (with T1)
|------|

or:

PL->BLL->DAL
|   |    |
v   v    v
DL (with T1)


I think we have covered that.


BLL can not be completely independent of DAL because BLL will have
to call DAL.

But BLL can be decoupled from DAL.

Decoupled means that BLL only knows about the API that DAL exposes
and nothing about the implementation inside DAL and as a result of that
the DAL implementation can be changed without any changes to BLL.

The minimum API that has to be exposed are:
* the data, either as in my examples classes or more general as
interfaces
* the various way to move data in and out which should definitely
be an interface
* some way to get the DAL implementation like factory class
(first example), reflection convention (second example) or
some IoC/DI framework

When don that way you can change:
- type of storage (database, XML)
- database type (SQLServer, Oracle, MySQL, DB2)
- persistence framework (plain ADO.NET, EF, NHibernate)
with no changes to BLL.

Arne
Arne_Vajhøj replied to csharper on 15-Sep-11 08:58 PM
It is actually not completely off-topic.

First he explains that using interface for decoupling
is used between all layers not just between BLL and DAL.

Then he gives an example with mock objects.

The reason why you can use mock objects is if you have an interface and
a configurable way to get a specific implementation.

In my examples I have used database and XML implementations.

But for unit test of BLL you can use a mock implementation.

Arne
Arne_Vajhøj replied to Big Steel on 15-Sep-11 08:59 PM
Interfaces should be used in any multi layered app.

It is not in any way web app specific.

Arne
Arne_Vajhøj replied to Registered User on 15-Sep-11 09:02 PM
Using interfaces inside DAL should not effect BLL-DAL coupling at all.

Arne
Arne_Vajhøj replied to Big Steel on 15-Sep-11 09:05 PM
If you do not do anything else then you just replace the BLL-DAL coupling
with two couplings BLL-SL and SL-DAL.

If you can decouple those two, then you can also decouple BLL-DAL. The
decoupling techniques are not specific for layers.


That is relying on the developer to enforce decoupling instead
of using the tools provided by .NET and C#.

Not good.

Arne
Arne_Vajhøj replied to Big Steel on 15-Sep-11 09:06 PM
You need either an interface or a very general abstract class.

Otherwise you get the coupling.

Arne
Big Steel replied to Arne_Vajhøj on 15-Sep-11 09:12 PM
But for the most part,  interfaces are  used a lot more in Web and SOA
applications. Windows based developers hardly even know an interface
even exist with VB.NET developers being the worst of them all. Windows
based developers hardly even know n-tier/multilayer even exist.
Arne_Vajhøj replied to Big Steel on 15-Sep-11 09:13 PM
Long example.

And with many good ideas.

But it still manage to couple the BLL and DAL.

new DataAccessLayer.Article()

is all it takes.

Want to change it? You need to edit BLL and rebuild!

Arne
Arne_Vajhøj replied to Big Steel on 15-Sep-11 09:16 PM
Some desktop app developers know about layers.

I can believe that the percentage of developers not knowing
about layers are higher for desktop apps than for many
other types of apps.

Mort!

Arne
Big Steel replied to Arne_Vajhøj on 15-Sep-11 09:22 PM
Just put a Service layer between the BLL and DAL, and they are flat-out
decoupled.

BLL
Sevice Layer
DAL

And the BLL knows nothing about the DAL.

The Service Layer knows about the DAL and the BLL knows and makes
requests to the Service layer to do CRUD with the DAL.

With each one of the layers having its own interface.

And I am not talking about any type of Web service or SOA service, but
rather a classlib project called Service Layer sitting between the BLL
and DAL classlib projects.
Big Steel replied to Arne_Vajhøj on 15-Sep-11 09:26 PM
You decouple the BLL and DAL by using a WCF service.

BL-Service layer
SL-WCF service.
WCF service-DAL



it is used all of the time.
Arne_Vajhøj replied to Big Steel on 15-Sep-11 09:35 PM
You can put 117 layers in between or none - it does not matter.

If you can decouple layers then you can decouple.

If you cannot decouple layers then you cannot decouple.

Arne
Arne_Vajhøj replied to Big Steel on 15-Sep-11 09:39 PM
You can do that.

If there are good reasons to do it. Usually it is much better to
expose business logic than data as services.

But it dos not solve the problem of decoupling two layers.


I believe that. There are a lot of new developers (and some
old developers that has not learned much) out there.

It does not make it good.

Arne
Big Steel replied to Arne_Vajhøj on 15-Sep-11 09:40 PM
That's if you have the BLL in contact with the DAL.

But being realistic here, in a logical tier separation with everything
on the same machine, it really does not make that big of a difference if
it is coupled or not coupled as long as it does what it needs for CRUD.

With all of it sitting on the Web server with a Windows Console
application using the same BLL and DAL on whatever server it is running on.

Actually, it works very well for the client I am working now with its
clients using specialized services created by the client I work for.
Big Steel replied to Arne_Vajhøj on 15-Sep-11 09:47 PM
I do not see the BLL and DAL being coupled, which is the point.


It is what it is.

And I see a lot more Web developers coming in one Dofactory and how to
architect solutions.

http://www.dofactory.com/Framework/Framework.aspx
Arne_Vajhøj replied to Big Steel on 15-Sep-11 09:57 PM
Lots of solutions work.

Working is a necessary but far from sufficient condition for
being good.

Arne
Arne_Vajhøj replied to Big Steel on 15-Sep-11 09:58 PM
No.

I think the point is to decouple layers.

Arne
Big Steel replied to Arne_Vajhøj on 15-Sep-11 11:30 PM
Whatever....
Registered User replied to Big Steel on 15-Sep-11 11:55 PM
With the repository pattern in mind I consider the service part of the
DAL. The service is more than just a wrapper for the chosen repository
implementation. The repository Interface can be as simple six methods:
Add, Save, Remove, FindBy(id), FindAll and a paged FindAll. Use the
query object pattern and add two more FindAll methods.

The service can contain DAL functionality not found in the repository
implementation. The methods it exposes can be as fine-grained as
needed.

The coupling between the BLL and the DAL model is tight. The same
entity objects and service are used regardless of the repository
Interface's implementation. Dependency injection provides loose
coupling between the DAL service and it is repository implementation.

Yes we are on the page, those types of services are not part of the
abstraction.


I would expect no less ;)

regards
A.G.
Registered User replied to Arne_Vajhøj on 16-Sep-11 09:42 AM
The client can create its DAL using DI with this code

IDataRepository dr = new XmlRepository();
DalService service = new DalService(dr);

or with this code

IDataRepository dr = new SqlRepository();
DalService service = new DalService(dr);

The service type and the Interface are both part of the DAL model; the
service is the public interface of the DAL (call it the interface to
the Interface). The DAL as a whole contains both the model and an
implementation of the model's Interface.

The examples show the tight coupling between the client and the DAL's
model. At the same time the examples also show loose coupling between
the client and the model's Interface implementation. Using the
Interface decouples the client from any specific DAL implementation.

regards
A.G.
csharper replied to Registered User on 16-Sep-11 11:30 AM
Thanks, that book seems to have a very good rating.  I am seriously considering buying. They'll thank you for your reference. :=)
csharper replied to Big Steel on 16-Sep-11 01:58 PM
Thanks for this example, getting a hang of it.
csharper replied to Big Steel on 16-Sep-11 02:31 PM
If I understand it, your PTO classes are basically POCOs, and they are in the DAL, correct?

Until Arne said that POCOs can be in any layer, I had assumed that POCOs are only supposed to be in the BLL layer.

And since the BLL will reference the DAL, the BLL has no problem referring to such POCO classes.

If

1) you have a service layer between the BLL and DAL, called it SL,
2) your PTO/POCOs are in the DAL,

then both the BLL and the SL will have to reference the DAL, and hence strickly speaking, one cannot say that the BLL knows nothing about the DAL. What do you think?
csharper replied to Arne_Vajhøj on 16-Sep-11 02:44 PM
e
t?

Yes, that helps me understand the architecture. I understand now that POCOs=
are more likely going to stay in a DAL. Wouldn't it work if we put our POC=
Os into a Common project, to which both the BLL and DAL can reference?
csharper replied to Arne_Vajhøj on 16-Sep-11 03:42 PM
t
) select
le
le

So, what is the solution? What should be done to avoid "new DataAccessLayer.=
Article()" in the BLL?
csharper replied to Big Steel on 16-Sep-11 04:28 PM
I am still perusing/researching your sample code. So the interface IArticle is defined in the DAL, and both the DAL and the BLL contain a class Article that implements DAL.Interfaces.IArticle, in other words, we have

DAL.Interfaces.IArticle
DAL.Implementions.Article : DAL.Interfaces.IArticle
BLL.Implementions.Article : DAL.Interfaces.IArticle

And BLL.Implementations.Article is a wrapper of methods defined in DAL.Implementations.Article.

Does this sound correct? Thank you.

I have not linked up the Service or Repository layer yet.
Big Steel replied to csharper on 16-Sep-11 04:31 PM
Nope.. And DTO is (Data Transfer Object). Each layer has its own set of
DTO(s) it uses. DTO(s) are passed through the layers.

http://en.wikipedia.org/wiki/Data_transfer_object

For the most part in dealing with a Web based solution with
UI/presentation layer/service layer/BLL/DAL, each layer has their set of
DTO(s). They all have the same name like DTOArticle with the same
properties in the object. The only thing different between all of them
is what namespace is the DTO located.

The DAL has reference to the objects on the Entity Framework Model.
Those EF objects are mapped to like DTO(s) in the DAL. EF Model object
Article is mapped to DTOArticle in the DAL.DTO namespace. The DAL DTO in
returned to a method in the BLL. The BLL has reference to the DAL and
can see those DAL.DTO(s).

The BLL has its own set of DTO(s) and the methods in the BLL maps the
DAL.DTO(s) to like BLL.DTO(s). The Service Layer has reference to the
BLL. The Service Layer can see the BLL.DTO(s), and the Service Layer
cannot see past the BLL.

I guess one could have a common classlib project for the DTO(s) that all
the layers have reference to and share. But in either case of DTO(s) in
each layer as opposed to a common classlib project of DTO(s) shared
between all layers, the SL knows nothing about the DAL it can directly
see. Each one of the layers can only see the layer it has reference to.

Of course I have DTO(s) in the Service Layer mapping BLL DTO(s) to SL
DTO(s). And DTO(s) in presentation layer mapping SL DTO(s)to PL DTO(s).

The UI could see the PL DTO(s) only since it only has reference to the
PL. Of course mapping would be reversed sending DTO(s) from PL down to
the DAL mapped back to EF entities to be persisted to the model.

That's just the way I do it. That's what I have seen being done or some
variation of it.

If you have BLL/SL/DAL, then how is the BLL going to know anything about
what is happening in the DAL if mapping between DTO(s) in each layer is
done?

In SOA, it would be UI/PL/SL/WCF service/DAL/EF model sending those
DTO(s) through the layers.

You can switch between a Web UI or desktop UI and it would not matter
from the PL and down as to what type of UI was being used.

That's how it is done with the Dofactory E-Commerce solution projects and
source code. Each, solution's UI and PL are interchangeable with the
backend of BLL/SL/WCF service/DAL/Model with the Model being
interchangeable in the DAL between Linq-2-SQL, ADO.NET EF, ADO.NET SQL
command objects with datareader and T-SQL or Oracle command objects and
datareader with P-SQL as the DTO(s) are sent through the layers.

http://www.dofactory.com/Framework/Framework.aspx
Arne_Vajhøj replied to csharper on 17-Sep-11 04:57 PM
Factory in DAL or plain reflection in BLL or a IoC/DI framework.

Anything that will allow you to change the DAL without changing
or rebuilding the BLL.

All of them will (at least for practical usage) use an interface.

Arne
Arne_Vajhøj replied to csharper on 17-Sep-11 05:07 PM
The POCO's returned by the DAL public API: yes. I would not generalize
to all POCO's.


That is also a possibility.

In another post I wrote this:


Let me elaborate a bit on them.

The first is where DAL contains the POCO that is exposed to
BLL. BLL then contains another POCO that is exposed to PL.
I think this is the model that steel has been proposing.

The second is where the DAL contains the POCO that is exposed
to both BLL and PL.

The third model is where the POCO's are in a separate DL that
is exposed to all of PL, BLL and DAL. That is what you are talking
about now.

All are possible.

Arne
csharper replied to csharper on 19-Sep-11 10:24 AM
Can you confirm if my understanding is correct?  In particular, is it the case that we have two Article classes, one in the BLL and one the DAL and both implement DAL.Interfaces.IArticle (see below)?

DAL.Implementions.Article : DAL.Interfaces.IArticle
BLL.Implementions.Article : DAL.Interfaces.IArticle
Big Steel replied to csharper on 19-Sep-11 11:12 AM
There is only one Article class and its implementation is IArticle.

This below is in the DAL it and implements its IArticle.

using DataAccessLayer.IDAL;

public class Article :IArticle
{

}

The calling object whoever is the calling object (object in the BLL in
this case ArticleManager) can only see the public properties and
or/methods defined by the DAL.IArticle implemented by the Article class.
The interface is the contract between the calling and called objects as
to what the calling object can see on the called object that is public.

This below is in the BLL and implements its own IArticleManager

using BLL.IBLL;

public class ArticleManager : IArticleManager
{

}

In this case, a Service Layer object can only see the public properties
and/or methods defined by the IArticleManager implemented by
ArticleManager in the BLL. It is the contract for any calling object in
the SL that wants to get/set public properties and/or call public
methods on the ArticleManager object.
csharper replied to Big Steel on 19-Sep-11 12:49 PM
Thanks, and I just realize that you are not using the Repository pattern like that of NHibernate.
Big Steel replied to csharper on 19-Sep-11 02:14 PM
nHibernate does not own the Repository design pattern. The Repository
pattern is a design pattern used in software development period. ADO.NET
Entity Framework can work with the Repository pattern. I have used
nHibernate and the Repository pattern at a company I contracted  and did
some contract work,  and I did not particularly care for it.
csharper replied to Big Steel on 19-Sep-11 02:24 PM
Thanks, I never meant that NHibernate owns it, I simply have to use the Repository pattern for my work. But I guess essentially, your service layer (SL) is much like the Repository, a layer between the BLL and the DAL, relaying data them.