How it works
This bulletin board is built entirely on ECS Rails — every screen you've seen maps to a component-based domain model.
Entities are composed from components
A Post isn't a wide table. It's an identity row
composed from small components — each its own table:
class Post < ApplicationEntity
component Title
component Body
component PublishState
component Likes
relates_to :author, User
end
post.title.text,
post.author, post.likes.increment! — the state and
behaviour live in the components, delegated onto the entity.
Markers → badges
The Moderator
and ★ Admin badges on
People are marker components. A user
is a moderator exactly when the row exists —
user.add(Moderator), user.moderator?. No STI.
Lazy by default
A component costs nothing until a
value differs from its default — no row, no query. A new person with no
bio has no bios row, yet user.bio.text still
works.
Query by composition
The posts list is
Post.with_component(PublishState, state: "published") — it
filters by what an entity is made of, and scopes to the entity type so a
shared component can't leak across types.
Relationships & join entities
relates_to :author, User
links a post to its author with no boilerplate. A
group membership is a join entity
carrying two relationships and a role.
Validation merges onto the entity
The Email component
validates its address; the error surfaces as
user.errors[:"email.address"] and reads naturally in the form.
Try adding a person with a bad email.