Search code examples
javajsonpojo

What is the best way to define a common Base Java Model for all Responses?


I have a need to format all REST responses as:

{
    "response": {}
    "status": "ERROR"
    "error": {
        "status": "ERROR",
        "errors": [
            {
                "type": "ERROR",
                "code": "E1001",
                "message": "This is error 1.",
                "reference": "TEST 1"
            },
            {
                "type": "ERROR",
                "code": "E1002",
                "message": "This is error 2.",
                "reference": "TEST 2"
            }
        ]
    }
}

Where the response is an arbitrary data (object, array; with arbitrary fields) different for each REST API endpoint.

What is the best way to define different response for each response Model ?

I tried this (and it works, but I'm not sure is it the right thing to do):

BaseResponseModel.java

public class BaseResponseModel {
    
    private String status = "SUCCESS";
    private List<ErrorExtendedModel> errors = new ArrayList<>();
    
    // Various constructors, getters and setters
    
}

and the subclass:

CustomerResponseModel.java

public class CustomerResponseModel extends BaseResponseModel {

    public List<Response> response = new ArrayList<>();

    public static class Response { 
    
        private String customerId;
        private String firstName;
        private String lastName;
    
        public Response(String customerId, String firstName, String lastName) {
        
            this.customerId = customerId;
            this.firstName = firstName;
            this.lastName = lastName;
        }
    
        // getters and setters of inner class
    }

    public CustomerResponseModel() {
        super();
    }

    public List<Response> getResponse() {
        return response;
    }

    public void setResponse(List<Response> response) {
        this.response = response;
    }
}

I used inner static class with fields in every subclass but I'm not sure is it the correct way to do.


Solution

  • In this case I wouldn't go for inheritance but rather go for composition and leverage generics. The advantage of this approach is that you don't require nested classes, you enforce the base REST model throughout your application while keeping the response type generic.

    BaseResponseModel.java:

    public class BaseResponseModel<T> {
        
        private T response;
        private String status = "SUCCESS";
        private List<ErrorExtendedModel> errors = new ArrayList<>();
        
    
        public BaseResponseModel(T response) {
            this.response = response;
        }
    
        // ...
        
    }
    

    CustomerResponseModel.java:

    public class CustomerResponseModel {
                
        private String customerId;
        private String firstName;
        private String lastName;
        
        public CustomerResponseModel(String customerId, String firstName, String lastName) {      
            this.customerId = customerId;
            this.firstName = firstName;
            this.lastName = lastName;
        }
    
        // ...
    }
    

    Example of the usage:

    public class RestApiController {
                
        public BaseResponseModel<CustomerResponseModel> getOneCustomer(String customerId) {      
            return new BaseResponseModel(new CustomerResponseModel(...));
        }
    
        public BaseResponseModel<List<CustomerResponseModel>> getAllCustomers() {      
            return new BaseResponseModel(List.of(new CustomerResponseModel(...), new CustomerResponseModel(...)));
        }
    
        // ...
    }