Implementing REST API using the layered architecture

In this article, we have demonstrated a systematic approach for designing REST APIs, using as example a Blog platform. In this article, we are going to show how implement APIs using a layered architecture with the Blog platform as example.
What is a layered Architecture?
Layered architecture is a way to organize systems in code units (or layers), each layer playing a specific role in the system. Although the layered architecture pattern does not specify the number and types of layers that must exist, most layered architectures consist of four standard layers: presentation, business, persistence, and database.

- Each layer has a specific role and responsibility within the architecture. For example, the presentation layer is responsible for handling all user interface and browser communication, whereas a business layer is responsible for executing specific business rules associated with the request.
- Layers are isolated from each other, having no knowledge of the inner workings of other layers.
- Each layer communicate only with the layer beneath it. This mean the presentation layer communicate only with business layer, and should not communicate directly with persistence layer.
- Data is flowing from one layer to another.
Request from client are received by the Presentation layer, transformed to Domain Objects are the are moving to lower layers.
Response are transformed from Domain Objects coming from lower layers.
Implementing Domain Objects
During the design phase, the Resources of the API Object Model that have been identified should be implemented as Domain Objects. Each Domain Object will contains the state of its associated Resource.
For the Blog API, we have identified as Resources Article
and Comment
. We will create Domain Objects for each of those resources, and it will look like:
class Article { val id: Long, val title: String, val author: String, val email: String, val publishedDate: Date, val status: String, val summary: String, val language: String val content: String } class Comment { val id: Long, val article: Article, val text: String, val author: String, val email: String, val commentDate: Date, val language: String, val sentiment: Float }
Layering REST API

Database Layer
The database layer responsibility is to store the Domain Objects. This responsibility is the same, regardless if your are using a Relational database like MySQL, a JSON database like Google Firebase Database or Cloud key/value pair database like DynamoDB.
In the Blog API, we will use MySQL, and each Domain Object is stored in a table:
- The table
T_ARTICLE
for storingArticle
. - The table
T_COMMENT
for storingComment
.
The script for creating the tables in the Database layer will look like this:
CREATE TABLE T_ARTICLE( id BIGINT AUTO INCREMENT, title VARCHAR(255), summary VARCHAR(255), content TEXT, language VARCHAR(2), author VARCHAR(100), email VARCHAR(100), status VARCHAR(100), published_date_time DATETIME, PRIMARY KEY (id) ); CREATE TABLE T_COMMENT( id BIGINT AUTO INCREMENT, article_fk BIGINT, author VARCHAR (100), email VARCHAR(100), text TEXT, language VARCHAR(2), comment_date_time DATETIME, FOREIGN KEY (article_fk) REFERENCES T_ARTICLE(id), PRIMARY KEY (id) );
Persistence Layer
This persistence layer responsibility is to provide an interface for accessing the database. This layer contains Repositories
or Data Access Object
, which implement the Create, Retrieve, Update and Delete (CRUD) operations.
It's common practice to use Object Relational Mapping (ORM) frameworks like Hibernate, Doctrine etc. to implement the CRUD operations without writing cumbersome SQLs statements.
In the Blog API, Article
and Comment
persistence will be handled respectively by ArticleRespository
and CommentRepository
, and their code will look like:
interface ArticleRepository { fun findAll(): List<Article> fun findById(id: Long): Article fun save(article: Article): Article fun delete(article: Article) } interface CommentRepository { fun findByArticle(article: Article): List<Comment> fun findById(id: Long): Comment fun save(comment: Comment): Comment }
Business Layer
The business layer responsibility is to implement the core logic of your business. Service
are the classes used in this layer to implement the business logic.
In the Blog API, Article
and Comment
business logic will be implemented respectively by ArticleService
and CommentService
. Here is an example of implementation of ArticleService
:
class ArticleService { val articleRepository: ArticleRepository fun create(request: CreateArticleRequest): Article { val article = createArticle(request) return articleRepository.save(article) } private fun createArticle(request: CreateArticleRequest): Article { ... } fun get(id: Long): Article { return articleRepository.findById(id) } fun delete(id: Long) { articleReponsitory.delete(get(id)) } }
Note that ArticleService
interacts only with ArticleRepository
. This shows that the Business layer communicates only with Repository layer.
Presentation Layer
The Presentation layer responsibility is to handle clients requests sent over HTTP. This layer contains Controllers
, that receive client requests, forward them to the Service layer and return results to clients.
The data exchanged between the Controller and the client are done in JSON (or XML) format, over HTTP. All modern programing language offer capabilities to automatically convert back and forth JSON (or XML) to Objects.
In the Blog API, Article
and Comment
controllers are implemented respectively in ArticleController
and CommentController
. Here is an example of implementation of ArticleController
:
class ArticleController { val articleService: ArticleService // POST /articles fun create(request: CreateArticleRequest): CreateArticleResponse { val article = articleService.create(request) return toCreateArticleResponse(article) } private fun toCreateArticleResponse(article: Article): CreateArticleResponse { return CreateArticleResponse(article.id) } // GET /articles/{id} fun get(id: Long): GetArticleResponse { val article = articleService.get(id) return toArticleResponse(article) } private to GetArticleResponse(article: GetArticleResponse) { return GetArticleResponse( .... ); } // DELETE /articles/{id} fun delete(id: Long) { articleService.delete(id) } ... }
Note that ArticleController
interacts only with ArticleService
. This shows that the Representation layer communicates only with Service layer.
Advantages to Layered architecture
Layered architecture enforces strong Separation of Concern, which provide the following advantages:
- Consistency: It provide a consistent pattern for organizing your application. So it's very easy any developer who is familiar with this architectural pattern to navigate through the code.
- Simplicity: Because each layer address only one concern, we end up with classes having functions with very few line of code, which simplify and improve the readability of the code.
- Maintainability: It's much simpler to modify individual layer of the system. The fact that layer are independant, change can be done with minimal impact to other layers.
Next Stories in the Serie
