6

Programmers, when it comes to talk about popular patterns in enterprise applications, preach that you should code against interfaces to remove strong relations between components; doing this will aid in changing concrete types while minimizing changes in other areas of the application. The underlying assumption is that interfaces are all about abstractions. When it comes to real life it is almost impossible. Lets use an example:

Lets say you currently use an ORM framework in your application, which you abstracted by applying the repository pattern. Now, can you really change ORM framework seamlessly?

public interface IRepository<T>
{        
    T GetById(int id);      
    void Add(T entity);
    void Remove(T entity);
    void Update(T entity);
    IQueryable<T> Query(Expression<Func<T, bool>> filter);                                
} 

Theoretically, this implementation can be applied to the rest of the application, but many questions remain about the actual application of this pattern:

  1. How does this pattern shield me from the internal process of libraries?
  2. How does the abstraction deal with the variation in exceptions thrown by each library?
  3. If I have selected an ORM which provides a caching mechanism internally and I later decide to switch to an alternative ORM with no such support, how does the pattern handle this?

So, from this, I believe that the project architecture is strongly influenced on the libraries that are used. Is there anything wrong with this? Am I assuming too much from this pattern?

1
  • Not really an answer, but I've always felt that if you're using an opinionated library you should speak its dialect instead of translating it. An ORM system shouldn't be wrapped because it's too opinionated and you'll lose too much in the translation. Commented Sep 5, 2011 at 19:48

5 Answers 5

5

AnyOne,

The interface you have shown is an example of a typical abstraction that sits between a client and a CRUD System. As with all patterns, there is a point where you need to recognize the product is being over engineered. Compensating a lack of domain knowledge by building in systematic flexibility is one of the many evils of software development. Your a programmer, not a prophet.

You mentioned caching. The interface you have shown makes no assumption that caching is used, so the interface is shielding the client code from this difference if you choose to switch. Performance wise, you may notice a difference, but the code won't care.

With regards to exceptions, you can build your own hierarchy that wraps specific exceptions. I believe the Spring framework takes this approach with JDBC templates as the JDBC API has only one exception - SQLException, which doesn't shield client code from JDBC driver specific issues, so Spring gives you an exception hierarchy that wraps most of the driver specific errors that can happen during runtime.

So, your choice of libraries may influence the performance and runtime behavior of your system, but general purpose interfaces should not care about specific features of an underlying implementation as that would result in what Spolsky referred to as a leaking abstraction.

3

What you describe is just another case of Joel's "Law of leaky abstractions". Read his great blog entry about this topic:

 http://www.joelonsoftware.com/articles/LeakyAbstractions.html

On the other hand, when you want different repository implementations to be interchangeable, just conferring to some interface is not enough, that's correct. At least, they have to follow the Liskov substitution principle. This principle says that your different subclasses of IRepository must behave all alike, for example, consistent use of exceptions for error handling,

Well, the ORM example is of course a tough case, since ORM's are often complex components where even things like performance requirements or memory overhead can make the difference between a component feasible for your project or not. The dependency inversion principle (that's the pattern you described) applies much easier to smaller components. And it applies much better to components you have under your own control - don't expect third-party components to be interchangeable easily if they are not especially designed for that purpose.

IMHO the real value of an interface IRepository is not the interchangeability of one ORM against another. The real value is that you can create a mock implementation for your repository allowing lightweight unit tests.

1

I would go a little further than the other commentators. ORMs are definitely not an abstract interface. There are dozens of dependencies and implementation specific behaviors. Small changes in the underlying model, or the underlying technologies ripple through to the application.

ORMs are a coders shortcut and provide OO coders with an interface that they are more comfortable with than raw *dbc. The resulting classes should not be thought of as interfaces.

In architectural terms an "interface" is an interface between logical components. In many cases a component consists of code and its underlying data model. You cannot separate a database and the code that maintains that database into two separate components, they are intimately linked. Any change to the data model will (and must) result in a change to the application code that maintains the model.

1

Libraries generally do not influence the architecture of the application. Frameworks, on the other hand, do. There is a large difference between libraries and frameworks. Libraries offer functionality that can be called on a as-needed base. Frameworks require your code to adhere to the concept of the framework, otherwise you will not really make use of the framework.

0

"In theory, there is no difference between theory and practice. In practice, there is."

Isolation via interfaces is a great theory. It rarely succeeds in practice, because the implementations that hide behind the interfaces often have behavioral differences that go way beyond the ability of the interface to hide. Even interfaces that strive have strong support and are highly isolatable (e.g., SQL) nonetheless fail to be completely isolated.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.