jeudi 26 avril 2018

Modeling API objects with respect to internal implementation

If you create a REST service that serves Banana objects, there are likely two different conceptual models of a Banana.

  1. What is used on the REST API you serve to the public
  2. What is used internally to the application, with possible DB fields that aren't exposed externally

Is it best to have a class that represents each, with available converters to go from one to the other?

class Banana {
  private String color;
}

class InternalBanana {
  private int id;
  private String color;
  private ZonedDateTime createdAt;

  public toBanana() {
    return new Banana(color);
  }
}

Some benefits I notice to this approach:

  • Marshalling to Json via jackson is simple
  • Banana can be used while test-driving the API code, which can't be said so much for InternalBanana.

Some cons:

  • Seems ripe for code duplication down the road
  • Converters of internal objects need to be updated any time the API is updated

Alternatively you could bundle everything into a single class and have it handle both cases:

class Banana {
  private int id;
  private String color;
  private ZonedDateTime createdAt;

  public String toApiJson() { ... }
}

Pros:

  • A single class, no duplication
  • Seems more robust to changes in the public API

Cons:

  • Rest API objects aren't actually modeled in the system anywhere
  • Marshaling to Json means writing a special ObjectMapper or toApiJson() method that only selects the appropriate fields
  • DB fields will often be null, requiring appropriate checks or use of Optional.

Which approach is likely to be the most efficient over the lifetime of a given service?

Aucun commentaire:

Enregistrer un commentaire