How Simple a Todo Backend App Can Be

DZone 's Guide to

How Simple a Todo Backend App Can Be

Learn how to use one of the newer frameworks in the Todo-Backend project, and significantly decrease your lines of code while not skimping on the quality of your app.

· Web Dev Zone ·
Free Resource

The Todo-Backend project provides an arena to showcase backend tech stacks. It defines a simple web API in the form of a to-do list and users can create their own identical APIs using various tech stacks.

As for now, there are over 80 Todo-Backend projects implemented in a wide range of tech/framework combinations, including JVM (Java/Scala/Kotlin/Closure), C#/.Net, Ruby, Python, PHP, Golang, Haskell, and a lot more. On each language/platform, there are one or more implementations based on different framework/data persist solution combinations.

I am the author of ActFramework, and just finished the Act implementation of Todo-Backend. As ActFramework is designed to boost developer productivity and enable express functions in less code without sacrificing readability, I was curious to see how Act's implementation compared to other implementations in terms of expressiveness, or simply speaking, the line of code. I did my homework to get the LOC data from some commonly used tech stacks and compiled the result into the following table:

Language/Platform Implementation Data Persistent Line of Code
Java/JVM ActFramework MongoDB 64
Java/JVM Spring4 + Boot Java Set 200
Java/JVM vertx MongoDB 241
Java/JVM Dropwizard Java Map 115
Java/JVM Jooby Java Map 231
Java/JVM SparkFramework PostgreSQL 348
Kotlin/JVM Rapidoid Java Map 81
Closure/JVM Closure PostgreSql 142
Scala/JVM Scala/Play2.5 PostgreSql 136
Golang Gin Map in memory 128
Golang stdlib In memory data structure 238
JavaScript/NodeJs express PostgreSql 130
Python webpy Array in memory 32
Python django sqllite 164
Ruby rails PostgreSql 311
PHP symfony2 sqlite 130 
Haskell Snap Sqlite 98
C#/.Net Asp.Net Web API In memory list 215
C#/.Net Asp.Net core ? (Entity Framework) 887
Swift Kitura MongoDB 473

Obviously, Act's implementation is very concise in comparison to other implementations, as it uses only 64 lines of code. Consider that normal Java implementations take up to 200 lines of code and that Act in the Java language was implemented in Dropwizard and used a HashMap as a data persist, which nearly doubled the size of Act's codebase - and it still had only 64 lines of code!

How ActFramework Can Make it So Compact

This is a simple project consisting of a model and a service (AKA controller). We will check the source code of these components to understand Act's implementation.

1. The Model

We chose MongoDB as our data persistent store. Act provides awesome integration with MongoDB through the act-morphia plugin, which relies on the official Morphia object document mapper layer.

An innovative feature Act brings to developers on top of Morphia is called AdaptiveRecord  which allows the backend developer to declare only the fields needed to participate in backend logic. For any fields required by frontend and not used in a Java app, just ignore them. Thus here is our entity model class for Todo :

@Entity(value = "todo", noClassnameStored = true)
public class Todo extends MorphiaAdaptiveRecordWithLongId<Todo> {

    // needs to define this property to make it comply with todobackend spec
    // unless https://github.com/TodoBackend/todo-backend-js-spec/issues/6
    // is accepted
    public boolean completed;

    // url is required as per backend test spec. However it is not required
    // to put into the database. So we mark it as Transient property
    public String url;

    // We will generate the derived property `url` after
    // saving the model and loading the model
    private void updateUrl() {
        url = Act.app().router().fullUrl(S.concat("/todo/", getIdAsStr()));

We don't need to declare all fields presented on the front end, e.g.  title order  and completed, which is declared in the source code because of this issue in the test spec.

Note the  url  is not part of the data to persist into our data store, instead, it is a derived property that concatenates the GET action URL path and the entity's id. We rely on Morphia's PostLoad  and PostPersist  lifecycle callback method to init the property.

2. The Service

It is very unusual to get a service class nested into the entity model class like what we did in this implementation:

@Entity(value = "todo", noClassnameStored = true)
public class Todo extends MorphiaAdaptiveRecordWithLongId<Todo> {

    // needs to define this property to make it comply with todobackend spec
    // unless https://github.com/TodoBackend/todo-backend-js-spec/issues/6
    // is accepted
    public boolean completed;


    public static class Service extends MorphiaDaoWithLongId<Todo> {

        public Todo create(Todo todo, Router router) {
            return save(todo);

        public Todo show(long id) {
            return findById(id);

However, in the TODO application, we think it is not a bad choice, because our TODO service only operates on a single resource type, i.e. the TODO item. Actually, it is encouraged in this case because:

  • The operation (service endpoint logic) and the data (entity model) sit together, meaning high cohesion in the app. The design mimics the Object Oriented Programming that encapsulates the data and the operation on the data into a single module.

  • It improves readability, as we don't need to switch between classes or even packages to find the data model when reading the service that manipulates the data.

Also, we can even take out the  @Produces(H.MediaType.JSON)  line if the test spec accepted and fixed this issue.

3. CORS Code

It is required by Todo-Backend that a showcase application must enable CORS. Thus we see code like:

Java 8 with Spring 4 Boot implementation

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    HttpServletResponse response = (HttpServletResponse) res;
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PATCH");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "x-requested-with, origin, content-type, accept");
    chain.doFilter(req, res);

Or Java with Dropwizard implementation

private void addCorsHeader(Environment environment) {
    FilterRegistration.Dynamic filter = environment.servlets().addFilter("CORS", CrossOriginFilter.class);
    filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
    filter.setInitParameter("allowedOrigins", "*");
    filter.setInitParameter("allowedMethods", "GET,PUT,POST,DELETE,OPTIONS,HEAD,PATCH");

However, we don't see any of these CORS relevant codes in the ActFramework implementation. The only thing we've done is added  cors=true  into the config file. This is another cool aspect of Act, it integrates utilities supporting common features in a web app including CORS, CSRF, etc.

In summary, ActFramework provides a flexible and powerful infrastructure that supports creating RESTful service in a much simpler way. With ActFramework, the developer just needs to focus on business logic, not plumbing.

java, jvm, web app development, web dev

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}