DEV Community

Vesin Dusko
Vesin Dusko

Posted on

ToDo app with Sifu code generator

As part of promoting DRY (don't repeat yourself), MDD (model-driven development), and code generator, I would like to show its full power on a real-life example.

We will implement a simple ToDo app with backend and client app. Let's start.

ToDo project

First we will define the project with basic informations.

todo project[
    url www.todo.com 
    namespace com.todo
    version 1 . 0 . 0 . 0
    environments prod
    developer(duskovesin)
]( 
Enter fullscreen mode Exit fullscreen mode

Backend

Now we will define the #backendpart of the app.

backend server[
        owners(duskovesin)
        template springboot(tests fabut)        
    ]
Enter fullscreen mode Exit fullscreen mode

We are starting with the definition of User and Todo #models, with a few fields and Todo Status #enums.

        UserRole enum(
            ADMIN
            MEMBER
        )

        Status enum(
            NOT_STARTED
            IN_PROGRESS
            DONE
        )

        User model(
            firstName text[min 1, max 40, searchable]
            lastName text[min 1, max 60]
        )

        Todo model(
            user User
            task text[min 1, max 255]
            date date
            status Status
        )
Enter fullscreen mode Exit fullscreen mode

We also need to specify the type of security that we want to use in the app, in this case, we are using role-based security on top of the User model with #username and #password as security #authentication.

        security(
            principal User
            role UserRole
            type username
        )
Enter fullscreen mode Exit fullscreen mode

Now we can focus on APIs. We will create two sets of #REST APIs.

UserApi

UserApi will have #CRUD API endpoints for manipulation with users in the system, that can be accessed only by ADMIN users, users endpoint to access to all users in the system, and one that Admin can use to check other users Todos.

UserApi api(

            user crud[model User, rest, secured ADMIN]

            users read[
                select from User
                rest
                secured ADMIN
            ]

            userTodos read[
                select from Todo
                     where Todo.user == user
                     orderable by Todo.task
                rest
                secured ADMIN
            ]
        )
Enter fullscreen mode Exit fullscreen mode

TodoApi

TodoApi with CRUD endpoints for users to be able to create ToDo and userTodos endpoint that will be used by any users to access ti his own ToDos.

        TodoApi api(

            todo crud[model Todo, rest]

            todos read[
                select from Todo
                     join User on Todo.user
                response list dto(
                    Todo.id
                    userUsername User.username
                    Todo.task
                    Todo.date
                    Todo.status
                )
                rest
                secured(ADMIN, MEMBER)
            ]
        )
Enter fullscreen mode Exit fullscreen mode

On average, for developers to implement all controllers, APIs, DTOs, models, enums and repositories, db-changelogs and all other parts of sping boot infrastructure code would take at least a few days...

WebApp

Now lets move to the client part of the app.

First, let's define the client app and specify which type of applications it will be generated to. Also, we will define, to which backend it will be connected. Also, we will define the application path and which page is the home page for admin and member users.

webapp client[
        owners(duskovesin)
        connectedTo backend
        template angular
        path /webportal
        home todosPage(ADMIN, MEMBER)
    ]
Enter fullscreen mode Exit fullscreen mode

Now we need to create two pages, one for users to organize their ToDos and the other one for admins to administrate with users.

ToDo page

ToDo page will have a list of user todos, add a button for creating new todo, edit button for opening edit form and delete button for todo deletions. All components will be connected with appropriate API calls which can be seen in the spec.

        todosPage page[
            path /todos
            secured(ADMIN, MEMBER)
        ](
            addTodo button {
                on click open createTodo(none) {
                    on closed do todos.load
                }
            }
            todos table[
                load TodoApi.todos
            ](
                editTodo button {
                    on click open editTodo(item.id)
                }
                deleteTodo button {
                    on click open deleteTodo(item.id)
                }
            )
        )

        createTodo form[
            submit TodoApi.createTodo
        ] {
            on success close
        }

        editTodo form[
            load TodoApi.readTodo
            submit TodoApi.updateTodo
        ] {
            on success close
        }

        deleteTodo form[
            load TodoApi.readTodo
            submit TodoApi.deleteTodo
        ] {
            on success close
        }
Enter fullscreen mode Exit fullscreen mode

Users page

The users page will be organized in the same way, with a small difference that the admin user will be able to access the ToDos of the other users.

        usersPage page[
            path /users
            secured ADMIN
        ](
            addUser button {
                on click open createUser(none) {
                    on closed do users.load
                }
            }
            users table[
                load UserApi.users
            ](
                viewUserTodos button {
                    on click fire ViewTodos(item.id)
                }
                editUser button {
                    on click open editUser(item.id)
                }
                deleteUser button {
                    on click open deleteUser(item.id)
                }
            ) {
                ViewTodos event(id integer)
                external {
                    on ViewTodos do userTodos.reload(event.id, none, none)
                }
            }
            userTodos table[
                input(*, *, *)
                load UserApi.userTodos
            ]
        )

        createUser form[
            submit UserApi.createUser
        ] {
            on success close
        }

        editUser form[
            load UserApi.readUser
            submit UserApi.updateUser
        ] {
            on success close
        }

        deleteUser form[
            load UserApi.readUser
            submit UserApi.deleteUser
        ] {
            on success close
        }
Enter fullscreen mode Exit fullscreen mode

Security page with sign-in form is automatically generated according to the best practices.

The amount of code that's necessary to write to do this basic implementation is:

Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Java                           148           2056             93           9193
TypeScript                      45            660            113           3083
HTML                            19              1              0           1114
XML                             14              0              4            881
JSON                             9              0              0            654
Maven                            3              0              4            246
Sass                            11             44             12            152
YAML                             3              2              0            102
JavaScript                       2              3              4             61
Markdown                         2             13              0             16
-------------------------------------------------------------------------------
SUM:                           256           2779            230          15502
------------------------------------------------------------------------------------


Enter fullscreen mode Exit fullscreen mode

Thanks to code generator sifu we are able to implement it in under 30 minutes...

You can check the code on github

Top comments (0)