Issue
I would like to have a collection of connections (REST, gRPC, RabbitMQ) for one or more different types of services. For example, with this configuration:
connections:
http:
- name: connectionA
host: localhost
port: 8081
endpoints:
- name: employees
httpMethod: GET
endpointType: EMPLOYEE
- name: departments
httpMethod: POST
endpointType: DEPARTMENT
grpc:
- name: connectionB
host: localhost
port: 8084
disableTls: true
endpoints:
- name: employees
endpointType: EMPLOYEE
When using the EmployeeService I would have an EmployeeHttpClient and an EmployeeGrpcClient, as well as a DepartmentHttpClient in the DepartmentService. Those clients would have the concrete implementation using the specific connection details, but the service shouldn't care about that.
What do you think about this approach?
What would be the correct way of doing it in Spring Boot?
Solution
It looks like this is a place where Strategy pattern can be used:
Strategy pattern is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.
Let me show an example via C#. I am sorry I am not Java guy, however I provided comments about how code could look in Java.
We need to have some common behaviour that will be shared across all strategies. In our case, it would be just one Get()
method from different data providers:
public interface IDataProvider
{
string Get();
}
And its concrete implementations. These are exchangeable strategies:
public class RabbitMQDataProvider : IDataProvider // implements in Java
{
public string Get()
{
return "I am RabbitMQDataProvider";
}
}
public class RestDataProvider : IDataProvider // implements in Java
{
public string Get()
{
return "I am RestDataProvider";
}
}
public class GrpcDataProvider : IDataProvider // implements in Java
{
public string Get()
{
return "I am GrpcDataProvider";
}
}
We need a place where all strategies can be stored. And we should be able to get necessary strategy from this store. So this is a place where simple factory can be used. Simple factory is not Factory method pattern and not Abstract factory.
public enum DataProviderType
{
RabbitMq, Rest, Grpc
}
public class DataProviderFactory
{
private Dictionary<DataProviderType, IDataProvider> _dataProviderByType
= new Dictionary<DataProviderType, IDataProvider>()
{
{ DataProviderType.RabbitMq, new RabbitMQDataProvider() },
{ DataProviderType.Rest, new RestDataProvider() },
{ DataProviderType.Grpc, new GrpcDataProvider() },
};
public IDataProvider GetInstanceByType(DataProviderType dataProviderType) =>
_dataProviderByType[dataProviderType];
}
and then you can get instance of desired storage easier:
DataProviderFactory dataProviderFactory = new();
IDataProvider dataProvider = dataProviderFactory
.GetInstanceByType(DataProviderType.Grpc);
string data = dataProvider.Get();
This design is compliant with the open/closed principle.
Answered By - StepUp
Answer Checked By - Timothy Miller (JavaFixing Admin)