Public demo — anyone can post. It wipes clean every 60 minutes. Next reset in

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.

Source & design docs on GitHub, the gem on RubyGems.