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)
](
Backend
Now we will define the #backendpart of the app.
backend server[
owners(duskovesin)
template springboot(tests fabut)
]
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
)
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
)
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
]
)
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)
]
)
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)
]
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
}
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
}
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
------------------------------------------------------------------------------------
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)