1 Star 0 Fork 0

greenlaw110 / todomvc-act

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

A TodoBackend Implementation in ActFramework

Sample web application implements todo-backend using ActFramework and MongoDB.

Verify the spec compliance at todobackend testing site

View the app on our demo site

To run locally:

//Make sure Mongodb is installed locally
$ mvn clean compile exec:exec

How simple a TodoBackend app can be written in X and Y framework

Just check the source code and table below

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 (only count files in src dir)
Haskell Snap Sqlite 98
C#/.Net Asp.Net core ? (Entity Framework) 887
Swift Kitura MongoDB 473

How can ActFramework make it so clean?

The Model

In this implementation we choose MongoDB as our data persistent store. Act provides awesome integration with Mongodb through act-morphia plugin, which relies on the official Morphia object document mapper layer.

A innovative feature Act brings to developer on top of Morphia is called AdaptiveRecord which allows the backend developer to declare only the fields needs to participate in backend logic. For any fields required by frontend and not used in 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
    @Transient
    public String url;

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

We don't need to declare all fields presented on front end, e.g. title, order and even 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 be persist into our data store, instead it is a derived property that concatenate the GET action URL path and the entity's id. We relies on Morphia's PostLoad and PostPersist lifecycle callback method to init the property.

The Service

It is very unusual to get 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;

    ....

    @Controller("/todo")
    @Produces(H.MediaType.JSON)
    public static class Service extends MorphiaDaoWithLongId<Todo> {

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

        @GetAction("{id}")
        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 one resource type, i.e. the TODO item. Actually it is encouraged because

  1. The operation (service endpoint logic) and the data (entity model) sit together means high cohesion in the app. The design mimic the Object Oriented Programming that encapsulate the data and the operation on the data into a single module.
  2. It improves readability, as we don't need to switch between classes or even packages to find the data model when I am reading the service operating on the data.

And of course we can choose this tight and clean pattern to organize our TODO showcase because Actframework provides the great flexibility to allow it put the action handler into any place given the action annotation is presented.

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

Where is the code support CORS

It is required by TodoBackend that showcase application must enable CORS. Thus we see the code like the following:

From Java 8 with Spring 4 Boot

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 from Vert.x and PostgreSQL

// CORS enabling
router.route().handler(CorsHandler.create("*")
        .allowedMethod(HttpMethod.GET)
        .allowedMethod(HttpMethod.POST)
        .allowedMethod(HttpMethod.OPTIONS)
        .allowedMethod(HttpMethod.DELETE)
        .allowedMethod(HttpMethod.PATCH)
        .allowedHeader("X-PINGARUNER")
        .allowedHeader("Content-Type"));

Or from Java with Dropwizard

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 code in the ActFramework implementation. The only thing we've done is add cors=true in the config file This is another cool stuff about 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 support creating RESTful service in a much simpler way. With ActFramework the developer just need to focus on business logic, not plumbing.

空文件

简介

Implement todomvc's backend using actframework 展开 收起
Java
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/greenlaw110/todomvc-act.git
git@gitee.com:greenlaw110/todomvc-act.git
greenlaw110
todomvc-act
todomvc-act
master

搜索帮助