leaky abstraction

Avoid Leaky Abstractions

leaking abstraction

 

It’s critical to create the ‘right’ kind of Abstractions—while it’s easy to develop awkward ‘leaky’ Abstractions.

Yesterday we covered off why Interfaces are Abstractions—a primer for today’s topic.

 

Abstractions are of immense importance in software development. They allow us to compartmentalise code and enable ‘pluggable’ software systems. However, we must carefully design our abstractions if they are to live up to these lofty expectations. 

In particular, it’s essential for us to carefully consider our abstractions and ensure they don’t leak implementation details.

Consider the following interface, ICustomerRepository:

  public interface ICustomerRepository
  {
     AbcFinance.SqlServer.Customer GetCustomer();
     void AddSqlCustomer(BusinessLogic.Customer customer);
     void UpdateCustomer(Guid customerId, AbcFinance.SqlServer.Customer customer);
  }

Interface ICustomerRepository represents an abstraction for a customer database or other persistence mechanism. You might also have noticed that a developer created it from a concrete SQL implementation. Here is ICustomerRepository again with highlights indicating its ancestry:

  public interface ICustomerRepository
  {
     AbcFinance.SqlServer.Customer GetCustomer();
     void AddSqlCustomer(BusinessLogic.Customer customer);
     void UpdateCustomer(Guid customerId, AbcFinance.SqlServer.Customer customer);
  }

There are several hints of ICustomerRepository‘s origin:

  • GetCustomer() returns a SQL model,
  • AddSqlCustomer() has ‘Sql’ in its name,
  • UpdateCustomer() takes a SQL model parameter.

ICustomerRepository is leaking SQL-ness implementation details like a sieve. But why is this a problem? Well, what if we wanted to replace our SQL Server customer database with a MongoDb one? Could we implement a Mongo DB version of ICustomerRepository?

Here is what that might look like:

  public class MongoDbCustomerRepository : ICustomerRepository
  {
     public AbcFinance.SqlServer.Customer GetCustomer() 
     { /* Mongo DB implementation */ }

     public void AddSqlCustomer(BusinessLogic.Customer customer) 
     { /* Mongo DB implementation */ }

     public void UpdateCustomer(Guid customerId, AbcFinance.SqlServer.Customer customer) 
     { /* Mongo DB implementation */ }
  }

Looks weird, right?! We are using SQL types with a non-SQL Mongo DB database! One of the methods has ‘Sql’ in the name, but it’s not a SQL implementation. No, this will not do at all.

 

We aspire to build interfaces that are not leaking implementation details. Superior abstractions use neutral naming and models. We want to design an abstraction so that we could develop multiple implementations for it.

Let’s try again with a revised version of ICustomerRepository:

  public interface ICustomerRepository
  {
     BusinessLogic.Customer GetCustomer();
     void AddCustomer(BusinessLogic.Customer customer);
     void UpdateCustomer(Guid customerId, BusinessLogic.Customer customer);
  }

That’s better. The methods have implementation-agnostic names. Customer is a neutral business logic model, devoid of implementation specifics, not a SQL model.

This improved ICustomerRepository plays nicely with both a SQL Server and a Mongo DB implementation:

  public class SqlServerCustomerRepository : ICustomerRepository
  {
     public BusinessLogic.Customer GetCustomer() 
     { /* SQL Server implementation */ }

     public void AddCustomer(BusinessLogic.Customer customer) 
     { /* SQL Server implementation */ }

     public void UpdateCustomer(Guid customerId, BusinessLogic.Customer customer) 
     { /* SQL Server implementation */ }
  }
  public class MongoDbCustomerRepository : ICustomerRepository
  {
     public BusinessLogic.Customer GetCustomer() 
     { /* Mongo DB implementation */ }

     public void AddCustomer(BusinessLogic.Customer customer) 
     { /* Mongo DB implementation */ }

     public void UpdateCustomer(Guid customerId, BusinessLogic.Customer customer) 
     { /* Mongo DB implementation */ }
  }

SqlServerCustomerRepository and MongoDbCustomerRepository work well with ICustomerRepository. Both implementations hide their database-specific details deep inside their methods.

Let’s Avoid Leaky Abstractions and design implementation-neutral abstractions instead.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply