How to change the design so that entities do not use injections?

I read and realized that objects (data objects for JPA or serialization) with injections into them are a bad idea. Here is my current design (all relevant fields have getters and setter, and serialVersionUID , which I throw for brevity).

This is the parent object, which is the title of the composition graph of the object. This is the object I'm serializing.

 public class State implements Serializable { List<AbstractCar> cars = new ArrayList<>(); List<AbstractPlane> planes = new ArrayList<>(); // other objects similar to AbstractPlane as shown below } 

AbstractPlane , and its subclasses are simple classes without injections:

 public abstract class AbstractPlane implements Serializable { long serialNumber; } public class PropellorPlane extends AbstractPlane { int propellors; } public class EnginePlane extends AbstractPlane { List<Engine> engines = new ArrayList<>(); // Engine is another pojo } // etc. 

In contrast, each specific type of vehicle requires the manager to have some behavior, as well as a certain form of data:

 public abstract class AbstractCar implements Serializable { long serialNumber; abstract CarData getData(); abstract void operate(int condition); abstract class CarData { String type; int year; } } public class Car1 extends AbstractCar { @Inject Car1Manager manager; Car1Data data = new Car1Data(); // (getter exists per superclass requirement) void operate(int i) { // logic looks weird but makes the example if (i < 0) return manager.operate(data); else if (i > 1) return manager.operate(data, i); } class Car1Data extends CarData { int property1; { type = "car1"; year = 1; } } } public class Car2 extends AbstractCar { @Inject Car2Manager manager; Car2Data data = new Car2Data(); void operate(int i) { if (i < 31) return manager.operate(data); } class Car2Data extends CarData { char property2; { type = "car2"; year = 12; } } } // etc. 

CarxManager are @Stateless beans that perform operations on the data (corresponding to CarxData ) provided to them. They themselves also use the injection of many other beans, and they are all subclasses of AbstractCarManager . There are O (100) car types and their respective managers.

The problem with State serialization is that serializing a list of abstract cars doesn't work very well with injections in subclasses. I am looking for a design that separates the injection from the data storage process.

My previous related questions: How to serialize entered bean? and How can I tell the CDI container to "activate" a bean?

+8
java java-ee design cdi ddd-repositories
source share
4 answers

The ability to remove a property, so it will not be matched by serializers. This can be achieved by getting programmatically.

 private Car2Manager getCar2Manager() { CDI.current().select(Car2Manager.class).get(); } 

I would not consider this clean solution, but it should be a workable "solution"

JPA @Transient may also work:

 @Inject @Transient Car2Manager manager; 

I have not tested this, so it may not work.

+2
source share

You can use the repository template. Put your business logic in the service and insert the repository (which abstracts the save mechanism) and the manager into it. The repository hides persistence implementation details from the business service, and entities are simply POJOs.

It will look something like this: Foo is the identifier of the object bar:

 public class CarService { @Inject CarRepository carRepository; @Inject CarManager manager; piblic void operate(final Foo foo) { Bar myBar = carRepository.retrieve(foo); manager.doSomethingTo(myBar); carRepository.persist(myBar); } } 

See also: Repository Template Step by Step Explanation , http://deviq.com/repository-pattern/ , Some frameworks, such as Spring Data JPA or deltaspike, already implement the repository template for you, all you have to do is provide an interface similar to the following and they generate an implementation in the background:

 @Repository public interface CarRepository extends EntityRepository<Car, UUID> {} 

Mark in response to your request in more detail. I am going to provide a reconstructed solution, because the example in the question really does not make sense to me and demonstrates quite a few anti-patterns that lead to problematic software.

To find a good solution to the problem, it touches on many different considerations, many of which are very large topics with many books written about them, but I will try my best to illustrate my thinking on their basis in order to solve the above problem.

And apologize because I have no doubt that you know about many of them, but I will assume limited knowledge for the sake of clarity.

The first step in solving this problem is not the code, but the model itself, development based on the model is widely covered in the book of Eric Evan, as indicated in the comments below. The model should lead the implementation, and also should exist at its level as part of a multi-level architecture and consists of objects, value objects and factories.

Developed Development Model

In the model asked in the question, we have something called a state that contains AbstractPlanes and AbstractCars. You use JPA to maintain a state that is actually the aggregate of your planes and cars. Firstly, to call any of the conditions in the software is a bad smell, because almost everything has some kind of condition, but by calling what we have here, which is the totality, the state makes even less sense.

How is one state different from another? Is one car part of one state and another part of another state, or is it so that all aircraft and cars belong to one state. What is the relationship between airplanes and cars in this scenario? How does the list of planes and the list of cars have anything to do with one state?

Well, if the state was actually an Airport, and we were interested in how many planes and cars were currently on the ground, then this might be the right model. If the state were an airport, it would have a name or identifier, such as an airport code, but that is not so ...

... In this case, it seems that State is an object that is used for ease of access to the object model. Thus, we effectively manage our model with implementation considerations, when we must do it the other way around and manage our implementation with our model.

Terms such as CarData are also problematic for the same reason, creating a Car object, and then a separate object to store its data is messy and confusing.

Failure to get the right model leads to software that is confusing at best and, at worst, completely dysfunctional. This is one of the biggest causes of failed IT programs, and the larger the project, the more difficult it is to do it.


Revised Model

So, from the model, I understand that we have Cars, and we have Planes, instances of which are all unique entities with their own identity. It seems to me that they are separate things, and therefore it makes no sense to transfer them into some kind of aggregate entity.

 public class Plane {...} public class Car {...} 

Another consideration is the use of abstract classes in the model, as a rule, we want to apply the principle to support composition over inheritance , since inheritance can lead to hidden behavior and can make the model difficult to read. For example, why do we have ProperllerPlane and EnginePlane? Of course, is a propeller just a type of engine? I greatly simplified the model:

 public class Plane implements Serializable { @Id private String name; private String model; private List<Engine> engines; 

An airplane is an entity with its attributes and identification. There is no need to have additional classes that do not represent anything in the real world for storing attributes. Currently, the engine object is an enumeration representing the type of engine used in the plane:

 public enum Engine { PROPELLER, JET } 

If the engine itself required identification, since in real life cycles engine serial numbers and things are tracked, we will change this to an object. But we do not want to allow access to it, except through an instance of the Plane entity, in which case Plane will be known as the aggregate root - this is an extended topic, and I would recommend Evan's book for more information on aggregates.

The same applies to the Car object.

 @Entity public class Car implements Serializable{ @Id private String registration; private String type; private int year; 

The above is all that you need, from what was provided in the question, for the foundation of your model. Then I created a couple of factory classes that handle instantiating these objects:

 public class CarFactory { public Car makePosrche(final String registrationNumber) { Car porsche = new Car(); porsche.setRegistration(registrationNumber); porsche.setType("Posrshe"); porsche.setYear(1986); return porsche; } } public class PlaneFactory { public Plane makeSevenFourSeven(final String name) { Plane sevenFourSeven = new Plane(); List<Engine> engines = new ArrayList<Engine>(); engines.add(JET); engines.add(JET); engines.add(JET); engines.add(JET); sevenFourSeven.setEngines(engines); sevenFourSeven.setName(name); return sevenFourSeven; } public Plane makeSpitFire(final String name) { Plane spitFire = new Plane(); List<Engine> engines = new ArrayList<Engine>(); engines.add(PROPELLER); spitFire.setEngines(engines); spitFire.setModel("Spitfire"); spitFire.setName(name); return spitFire; } } 

What we are doing here is a separation of problems, because in accordance with the principle of shared responsibility, each class should really do only one thing.


Now that we have a model, we need to know how to interact with it. In this case, we will most likely use JPA for cars in a table called Car and Planes. We would provide access to these persistent objects through repositories, CarRepository and PlaneRespository.

Then you can create classes called services that introduce repositories (and all you need) to perform CRUD (Create Read Update Delete) operations on instances of cars and aircraft, and this is also the moment when you can apply your business environment logic to these. For example, your method:

 void operate(int i) {..} 

By structuring your code in this way, you separate the model (objects and value objects) from how they are stored (stores) from the services that work with them, as indicated in your question:

I am looking for a design that separates the injection from the data storage process.

+8
source share

What is an entry point? Is it a web application, a leisure service, a soap service, or an event planner?

Injection frameworks almost always share data and service. Data is always a POJO that does not contain absolutely no business logic. Here, assuming this is a leisure service, I will do the following:

 public class SSOApplication { public class State implements Serializable { List<AbstractCar> cars = new ArrayList<>(); List<AbstractPlane> planes = new ArrayList<>(); // other objects similar to AbstractPlane as shown below } public abstract class AbstractPlane implements Serializable { long serialNumber; } public class PropellorPlane extends AbstractPlane { int propellors; } public class EnginePlane extends AbstractPlane { List<Engine> engines = new ArrayList<>(); // Engine is another pojo } public abstract class AbstractCar implements Serializable { long serialNumber; abstract CarData getData(); } public static class CarData { String type; int year; } public class Car2Data extends CarData { char property2; { type = "car2"; year = 12; } } public static class Car1Data extends CarData { int property1; { type = "car1"; year = 1; } } public static class Car1 extends AbstractCar { @Override CarData getData() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } } public static class Car2 extends AbstractCar { @Override CarData getData() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } } public static interface CarManager<T extends CarData> { void operate(T car, int index); default boolean canHandle(T carData) { final TypeToken<T> token = new TypeToken<T>(getClass()) { }; return token.getType() == carData.getClass(); } } @ApplicationScoped public static class Car1Manager implements CarManager<Car1Data> { public void operate(Car1Data car, int index) { } } @ApplicationScoped public static class Car2Manager implements CarManager<Car2Data> { public void operate(Car2Data car, int index) { } } @ApplicationScoped public static class CarService { @Any @Inject private Instance<CarManager<?>> carManagers; public void operate(int index, AbstractCar car) { final CarData carData = car.getData(); final CarManager<?> carManager = carManagers.stream() .filter((mng) -> mng.canHandle(carData)) .findFirst() .orElse(IllegalArgumentException::new); carManager.operate(carData, index); } } } 
0
source share

If you could change your flow than possible, you could do something like this:

 class Car1InnerService { @Inject Car1Manager manager; void operate(int i, Car1 car) { if (i < 0) return manager.operate(car.getData()); else if (i > 1) return manager.operate(car.getData(), i); } } } 

I introduced some internal service that will work on Car1 and use Car1Manager for this. Of course, your AbstractCar class will also lose the method to work , because now your service will process it. Now, instead of calling car1.operate (i) , you need to call through the Service like this:

 public class SampleCar1ServiceUsage{ @Inject Car1InnerService car1InnerService; public void carManipulator(List<Car1> carlist){ int i = 0; //I don't know why you need this param therefore i just increment it for(Car1 car: carlist){ car1InnerService.operate(i, car); i++; } } } 

Of course, you should introduce similar functionality for all other AbsractCar children (perhaps even if necessary, extract an abstraction, for example, AbsractCarInnerService, which will determine the method to work or any interface that will do the same, no other solid methods are needed in mute). However, this answer is still related to @Justin Cooke's answer and, in my opinion, you definitely need to check the templates that he mentioned in his post.

0
source share

All Articles