1

I think this is a common design problem, but I keep scratching my head every time I face it.

On one side, we have our API which sends or receives simple structures aimed to be consumed by web apps, mobile etc. We could potentially have multiple APIs aimed for different devices. On the other side we have our internal domain model which may or may not match the data store entities.

Let assume the following POJOs to assist the discussion.

@POST
public addBlogPost(BlogPostApi blogPost) { ... }

// api model
public class BlogPostApi {
  ...
}

// domain model
public class BlogPost {
  ...
}

// db model
public class BlogPostEntity {
  ...
}

First, we could use the same model in all three layers. While that could be a solution for very simple services, it's usually not a good idea since all the layers are implicitly coupled. So, I'll assume we want to use a different model object per 'layer'.

One approach I've seen widely used is to create adapters between layers. While that solves the problem, it creates an explosion of classes you need to maintain. In the (common) case when you add a new field to the data store which needs to get exposed on the UI as well, 3 POJOs + 2 adapters need to be changed. There's always the risk of forgetting to change one of those. This process could be automated slightly, with the use of an object mapper library.

Another approach I've heard is the use of interfaces. Two or more classes implement the same interface which exposes the get/set methods for 'common' fields. This removes the risk of forgetting to change something, but it creates coupling. Also, I find this use of interfaces wrong, since interfaces should expose functionality.

I'm leaning towards the object mappers. Does anyone know of a olution for this problem?

  • 3
    No matter how many abstractions or layers do we have. There will be always coupling somewhere. – Laiv Jul 18 '17 at 22:06
  • Two considerations. 1) by having 3 models you already achieved a high gradee of deocupling. 2) the more decoupling you want, the more components you get. The alternative is worse: reflection. – Laiv Jul 18 '17 at 22:28
  • Interfaces, and switching to weakly typed languages (a similar solution) don't actually decouple calls, they only obfuscate the calls. The real problem is when a lower layer makes a call back to an upper layer. This needs to be eliminated entirely in some cases. A more functional approach is usually the solution. – Frank Hileman Jul 19 '17 at 2:03
  • please don't cross-post: stackoverflow.com/questions/45177691/… "Cross-posting is frowned upon as it leads to fragmented answers splattered all over the network..." – gnat Jul 19 '17 at 5:51
  • @gnat thanks for clearing that out for me :) I was almost certain it's not a good practice. – Dimitrios K. Jul 19 '17 at 8:51
1

I think adapters is the right approach.

it creates an explosion of classes you need to maintain. In the (common) case when you add a new field to the data store which needs to get exposed on the UI as well, 3 POJOs + 2 adapters need to be changed

That's the effect of having a loosely-coupled system, but you're only describing the negative effect of the loose coupling. What happens when you want to add a new field to the data store, but you don't want to expose it on the UI? This design will allow you to support this use case. It also supports cases where you want to store things in the database differently than how they look in the domain model which is a fairly common need. For example, I like to have auto-increment numeric ids on all my database tables for foreign keys and join efficiency, but often the domain model may use some other unique field as its identifying field. With the three model approach I don't have to leak the db id to the domain layer.

  • Good answer. Id also add an explosion of classes is something which could honestly happen with any OO language in general. It can just be the case where you think you need one thing and BANG you have 10 classes you didn't expect. – Rhys Johns Jul 18 '17 at 22:47
  • 2
    Adding a new field to a data store does not automagically make it appear on any UI that I know of. – Robert Harvey Jul 19 '17 at 0:04
  • @RhysJohns an explosion of classes is always a warning sign of bad design. Are your design decisions based on logic or learned "principles"? – Frank Hileman Jul 19 '17 at 16:37
  • @Samuel I do understand the benefits of having decoupled layers. The only solution I'm aware of is to have adapters between layers. But, a design where I need to update every value class and all the adapters every time I need an end to end change feels bad. I think this design just moves the coupling problem to a different place, i.e. the adapters. I'm just asking if there's a better, less coupled design. – Dimitrios K. Jul 19 '17 at 19:45
  • 1
    @DimitriosK The adapters don't have to be coupled. I would choose to couple them to the models because it's easier to read and understand, but you could just as easily use a reflection-based adapter that isn't coupled to any of the models. Adding and removing fields would be a configuration change in that case (the config is coupled to the models). My answer doesn't discourage the use of generic adapters. – Samuel Jul 19 '17 at 23:43
0

Although the OP is referencing Java, I have a C#-based solution that directly applies. You can use the MVC design pattern to completely decouple your UI, BL, and DAL through interfaces and adapters. I have implemented such a solution with the GPS.SimpleMVC package on GitHub. You can read more about it at http://simplemvc.gatewayprogramming.school.

Now, the meat and potatoes comes from using Interfaces to define the views and the data/actions associated with those views. You can choose to couple the views to Models, or decouple the views through logic in the controllers. The controllers accept instances of views as input and register to the events exposed by those views. For example, if you need to tell the controller to load some data, you use a delegate (event) to signal to the controller to do some work. The controller then is responsible for creating the Models. The Models are responsible for exposing a DAL via a common interface. Inside the POCO Models are code to load an adapter based on configuration using Dependency Injection. The adapter has the code to perform the data access.

This completely decouples the three layers from the implementation details of each other. The strong coherence comes from the appropriate use of interfaces.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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