Majestic Monolith 2.0

Majestic is the monolithic web app lein template I created a few months ago, I’ve decided to change some things around in version 2.0, and here are the changes:
- A focus on features over tech. Folders are related to features of your app, files are related to technology, and the file names are hilariously explicit
- More separation between business logic (validation/prep before and after db interaction) and actual yesql functions.
- SQL generation for CRUD
That’s only three changes but it’s still a lot to take in. Here’s some more detail.
Features over tech
The first thing is that folders that used to say components (views), logic (models + db), and routes have been turned inside out. So now you’ll see folders like this:
├── src
│ └── your-ns
│ ├── sessions
│ │ ├── html.clj
│ │ ├── http.clj
│ │ └── logic.clj
│ ├── users
│ │ ├── db.clj
│ │ ├── html.clj
│ │ ├── http.clj
│ │ └── logic.cljThis makes it much easier to find what you’re looking for.
How do I find where a user gets inserted into the db?
users.db/insert<!
How do I find where a user gets validated before that?
users.logic/validate
What function gets called when a new user tries to sign up, or what happens on POST /users?
users.http/create!
What happens when I visit /login?
users.http/form which calls users.html/form
That’s the other major change, instead of hiding technology behind different abstract words, I just call it what it is, routes are now http, models are db and logic files, and components are HTML. This makes it really straightforward to find out which part of the code does what thing.
Separation between database functions and business logic
The goal of this change is to separate code that acts on the data from the code that talks to the database. It’s better for testing, the less tests that rely on a database, the faster they go.
So before where everything was in one file, this time there are two files, db.clj and logic.clj. So for users, there might be some code to validate emails and validate that the password matches the confirm password on signup, that code goes in logic, not db. After the logic, the code that calls insert on the database is in db and is usually generated by yesql from resources/sql/users.sql. That part is kind of hard to follow, because it’s a macro defqueries that generates the functions from the sql, but I think the trade off is worth it.
SQL generation
Majestic 1.0 already had migration support through ragtime but this got a swift kick in the butt. There’s a few new command line commands:
lein db/migrate # this migrates the database forward
lein db/rollback # this rolls back the database one migration
lein db/migration name # this creates a new migration
lein db/crud table # this generates a yesql compatible sql file!Better organization for more code generation in the future 😏. But the main thing is lein db/crud. This command generates sql for you! So let’s say you make a new table with
lein db/migration create-tagsWrite the migration for creating your table
{:up ["create table tags (id uuid primary key, name text, created_at timestamp);"]
:down ["drop table tags;"]}Then run the migration with
lein db/migrateAfter that you’ll be pleasantly surprised to not write out a bunch of boilerplate sql because you can run this
lein db/crud tagsNow you have four functions that let you interact with your new table!
-- name: insert<!
insert into tags (id, name, created_at)
values (:id, :name, :created_at)-- name: update<!
update tags
set name = :name
where id = :id-- name: delete<!
delete
from tags
where id = :id-- name: find-by-id
select *
from tags
where id = :id
Don’t forget to wire it up in tags/db.clj
(ns your-ns.users.db
(:require [yesql.core :as yesql]
[your-ns.db :as db]))
(yesql/defqueries "sql/tags.sql" (db/yesql-conn))Wrapping Up
Those are the major changes in majestic 2.0, codename “towards a framework”. I’m hoping in majestic 3.0 to add more code gen that creates a basic set of files allowing you to crud a new table from the browser which admittedly will be very similar to rails, which I don’t think is a bad thing.
