A microservice is a piece of business functionality with clear interfaces. Implementing an Microservice around an R application, allows its integration to platforms such as Android applications, Python applications, and commercial off-the-shelf products (COTS) products. This is because, a typical microservice is a process that communicate over a network throughout HTTP requests. The caller, whether it is a Website, Android application or even another R application, is neither aware nor care about the programming language that powers the service.
Caution: Adding a Microservice increases complexity. You must have good reasons to do so.
Tip:
Good reasons to add a Microservice:
Scalability for non-uniform traffic
Error resilience
Separate deployments
Complete isolation
Different requirements
We can write a microservice as an R extension, specifically in a pseudo-package manner. In this mode, we take advantage of well-documented practice and mature toolkit designated to develop R packages. To be clear, our microservice is not an R package, rather it is built as an R package. Writing a microservice as an R package means:
While development of microservices and R packages share folder structure and toolkit, they have a distinctly different purpose. In short, R packages “serve” microservices, but not the converse. Rather, microservices are stand-alone applications that serve a user or another system. Microservices are built primarily be practitioners.
We can separate the concern of microservice functions into two:
Core functions belong under the R
folder. For example, the function demo$class_input
(see excerpt) is part of R/demo.R
. This function receives any object and returns its class. It can operate without an API, e.g. calling demo$class_input(mtcars)
returns data.frame
.
API function, including entrypoints and endpoints, are stored outside the R
folder. In this demonstration they are stored under inst/entrypoints
and inst/endpoints
, respectively. We discuss in entrypoints and endpoints in length in subsequent documentations. For now, an example for an endpoint that wraps demo$class_input
is a nameless function located under inst/endpoints/plumber.R
:
function(x = NULL){
x <- x %>% jsonlite::fromJSON(flatten = TRUE)
demo$class_input(x)
}
As you can see, the nameless function knows about the existence of demo$class_input
, but not the converse.