The purpose of this entire project is to create an application that adheres to SOLID principals as much as possible, as well as demonstrate how to create a collection of microservices that leverage our algorithm libraries using Java and Spring.
There are two functional libraries:
-
Statistics: Contains a collection of statistical functions including a linear regression model.
-
Financial: Contains a collection of financial functions including models for estimating 401(k) retirement (both contributions and distributions)
The libraries are designed around SOLID principals, meaning that the implementation is separated from its interfaces, both logically and physically. This enables software to consume from the interface, while the implementation is abstracted away in a separate jar.
Moreover, each of the functional libraries are packaged separately, allowing for software
to use only what it needs, rather than relying a monolithic collection of code that
won't be used. That said, both of the libraries share a common "model", which are interfaces
that define a Parameter, Coefficent, Function, and Model, as well types of data with
Scalar[1], Vector and Matrix. By creating a separate set of interfaces for the model,
we can easily extend our code to create new functional libraries in the future.
As a consequence, several design patterns are used that allow access to the underlying implementation from the interfaces.
- Service Pattern: This is a pattern introduced by Java that uses the
java.util.ServiceLoaderto load implementations of a specified interface. - Factory Pattern: Factory methods are used to create instances of functions, models, parameters, coefficients and data structures.
The two functional libraries are broken into 7 separate projects:
- algorithm-model: Contains the interfaces that will be used by all functions
and model implementations. It exposes a
ModelProviderinterface that is used as the access point for concrete implementations via a Service Pattern. TheModelProviderexposes four factory interfaces used to createParameter,Coefficient,VectorandMatrixinstances. - algorithm-model-impl: This is a reference implementation of the
algorithm-modelinterfaces. - function-commons: Are a set of common abstract classes used by
functions and models. In addition, it defines interfaces for a
FunctionProviderandFunctionFactorythat specific function libraries must extend. In essence, function-commons is a bridge between the model, and the functional libraries. - statistics-functions: Defines the interfaces for all statistical functions
and extends the
FunctionFactoryandFunctionProviderinterfaces from function-commons that provide an entry point into the library. - statistics-functions-impl: This is the concrete implementation of the statistics-functions interfaces.
- financial-functions: Defines interfaces for all financial functions, and,
like statistics-functions, extends the
FunctionFactoryandFunctionProviderinterfaces from function-commons to provide entry points into the functions. - financial-functions-impl: Concrete implementation of the financial-functions interfaces.
The following diagram shows the dependency graph
To use either of the functional libraries, you only need to include the implementation jars for the algorithm-model and the specific functional library you want to use. For example, if you want to use the statistics library you would add the following to your dependencies:
- Note: I'm using property interpolation here as generic way of defining the artifact version.
Maven
<dependencies>
<dependency>
<groupId>io.xmljim.algorithms</groupId>
<artifactId>algorithm-model-impl</artifactId>
<version>${algorithm-model.version}</version>
</dependency>
<dependency>
<groupId>io.xmljim.algorithms.functions.statistics</groupId>
<artifactId>statistics-functions-impl</artifactId>
<version>${statistics-functions.version}</version>
</dependency>
</dependencies>Gradle
dependencies {
implementation("io.xmljim.algorithms:algorithm-model-impl:$algorithm-model.version")
implementation("io.xmljim.algorithms.functions.statistics:statistics-functions-impl:$statistics-model.version")
}Since the dependencies already include the interfaces, these will be included by default.
As a reference implementation of the algorithm libraries, I've created a set of microservices that work together to define a retirement calculator. Each microservice defines a set of endpoints that perform specific operations.
This application leverages several key components from Spring:
- Spring Web MVC
- Spring Netflix Eureka - for service discovery and registration
- Spring Config - for centralized configuration
- Spring API Gateway for creating a core set of endpoints and a homogeneous API from our microservices
- Spring Security
- Spring JPA - for storing stock and CPI data
- Spring Hystrix
- Spring Resilience4J
[1] Scalar isn't an interface, but it is a final class that extends Number, and includes several
static methods for creating instances.
