简体   繁体   中英

What is the best practice to construct a Domain Object depending on external calls?

I am trying to build an object that gets its data from external APIs. I will try to explain it with an example:

First of all, I get a POST body in my API to create an object:

class CarRequestBody {
    private String colorId;
    private String engineId;
    //Constructors, etc.
}

Then with this info I need to build a detailed instance of the Car. Feeding the data from external services:

ColorDetails color = colorApiClient.getColor(colorId);
EngineSpecifications engine = engineApiClient.getEngine(engineId);

And finally I build my Domain Object with all this info.

So I would like to know what is the best practice in order to build the instance. I have thought in 3 different ways:

1 - A method in CarService like this:

public Car createCar(CartRequestBody body) {
    ColorDetails color = colorApiClient.getColor(body.getColorId);
    EngineSpecifications engine = engineApiClient.getEngine(body.getEngineId);
    Car car = new Car(color, engine);
} 

2 - Feed the data in the constructor:

public Car(CarRequestBody body) {
    this.color = colorApiClient.getColor(body.getColorId);
    this.engine = engineApiClient.getEngine(body.getEngineId);
}

3 - In the getters of the domain class:

class Car {
    private ColorData color;
    private EngineSpecifications engine;
    //Constructor

    public ColorData getColor() {
        if (color == null){
            return colorApiClient.getColor(colorId);
        }
        return this.color;
    }
    ....
}

Is there some design pattern for this scenario?

I would suggest using the Builder design pattern.

Here you can see the code-

Car.java

 public class Car{   
   private ColorDetails colorDetails;
   private EngineSpecifications specifications;

  private Car(CarBuilder builder){
      this.colorDetails = builder.colorDetails;
      this.specifications = builder.specifications;
  }

  public static class CarBuilder {

      private ColorDetails colorDetails;
      private EngineSpecifications specifications;

      public CarBuilder withColor(ColorDetails colorDetails) {
        this.colorDetails = colorDetails;
        return this;
    }
     public CarBuilder withSpecifications(EngineSpecifications 
          specifications) {
        this.specifications = specifications;
        return this;
    }
    
   public Car build() {
        Car car = new Car(this);
        return car;
       }
   }
 }

// Client code

 public class Client{

  ColorDetails color = colorApiClient.getColor(colorId);
  EngineSpecifications engine = engineApiClient.getEngine(engineId);
 Car car = new Car.CarBuilder()
          .withColor(color)
          .withSpecifications(engine)
          .build();
   }

If you aim to keep your design clean, your domain class Car should be aware only of the objects like ColorData and EngineSpecificationsonly that it requires to function properly.

It's not a good idea to inject apiClient into the Car class because it's unrelated to its functionality and will increase coupling .

Also, what if an API call will fail, do you really want to add the plumbing code that will handle this scenario into a constructor?

Similarly, I don't see an advantage in defining a constructor that expects CarRequestBody .

The cleanest approach will be to keep the constructors of your domain classes simple and free from objects that are not needed for them to act. And if you want to generalize the process of instantiation of these domain classes, you can introduce utility methods that will take care of it.

What would be wrong with a simple constructor? I don't know Java, but would be this in C#:

class Car {
   ...
    public Car(Color color, Engine engine) {
   ... // set properties with the parameters
   }
}

Oh, and if you want to do some Decoupling, please don't use the same objects in your Domain as in API. So you will need some kind of Mapper object.

// EDIT

Since you are using a request, you could make a requesthandler class. If you use Inversion of Control or a Mediator you could connect the request to the handler in that way. So you could look into those design patterns. You could also look into Clean Architecture to separate your domain code from external systems (API's in this case)

public CarRequestHandler : IRequestHandler<CarRequest> {
    public CarRequestHandler(IColorRepository colorRepository, IEngineRepository engineRepository) { 
this.colorRepo = colorRepository; //etc.}
    public Handle(CarRequest request) {
// call repositories with the ids and create the domain object and stuff
       
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM