Skip to content

Jaocodigos/obj-calisthenics

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 

Repository files navigation

obj-calisthenics

This will be a simple project that "simulate" a Expense Approval Engine workflow. The main objective here is apply all 9 concepts of Object Calisthenics.

It intentionally avoids frameworks, databases, and UI concerns. The focus is business rules, object collaboration, and code readability.

Goal

The main goal of this project is to:

  • Apply Object Calisthenics rules in a realistic domain

  • Model behavior instead of data

  • Build small, expressive, and immutable objects

  • Avoid anemic domain models and procedural logic

This is not a CRUD application.

Definition

Object Calisthenics is a set of programming rules proposed by Jeff Bay to encourage better object-oriented design through deliberate constraints.

Rather than focusing on frameworks or architectural patterns, Object Calisthenics emphasizes:

  • Object behavior over data structures

  • Encapsulation and responsibility-driven design

  • Readability through simplicity and discipline

The goal is not to follow the rules blindly, but to use constraints as a training tool to expose design flaws and improve object collaboration.

By applying these rules, developers are forced to:

  • Break down large classes into smaller, meaningful objects

  • Replace conditional logic with polymorphism and delegation

  • Avoid primitive obsession and anemic domain models

  • Write code that communicates intent clearly

This project applies Object Calisthenics intentionally and consistently as a way to explore clean domain modeling and object-oriented thinking.

Why use those concepts?

Object Calisthenics is particularly effective in domain-driven projects because:

  • Business rules naturally map to object behavior

  • State transitions become explicit and controlled

  • Design mistakes surface early and are easier to correct

This repository treats Object Calisthenics not as a strict rulebook, but as a design lens to guide decisions and encourage high-quality code.

OC Principles

  1. One level of indentation per method

Each method represents a single business intention.

Complex logic is not nested. Instead, it is delegated to collaborating objects (Value Objects, Policies, Collections).

This keeps methods short, readable, and easy to reason about.

  1. No else keyword

Decision paths are expressed through:

  • Guard clauses

  • Polymorphism

  • Domain objects answering questions about themselves

This avoids deeply nested conditionals and keeps decision logic explicit and linear.

  1. Wrap all primitives

Primitive values (strings, numbers, dates) are never used directly to represent domain concepts.

Examples:

  • Money instead of float

  • ExpenseStatus instead of str

  • Role instead of raw strings

This prevents primitive obsession and allows business rules to live next to the data they govern.

  1. First-class collections

Collections are never exposed or manipulated directly.

Instead, they are wrapped inside dedicated domain objects that control:

  • validation

  • access

  • behavior

Example:

  • ApprovalHistory encapsulates a list of ApprovalEntry objects and defines meaningful operations over it.
  1. One dot per line

Objects communicate through behavior, not through navigation chains.

Instead of:

expense.history.entries[-1].approver_id

The model encourages:

expense.last_decision()

This enforces proper encapsulation and reduces coupling.

  1. Use descriptive names

Names favor clarity and intent over brevity.

Classes and methods are named after domain concepts, not technical patterns:

  • ApprovalPolicy

  • ApprovalEntry

  • ExpenseStatus

  • ApprovalHistory

If a name feels long, it usually means the concept is important.

  1. Keep objects small

Classes are intentionally small and focused.

Each object has:

  • one clear responsibility

  • a limited reason to change

If a class grows too large, it is treated as a design signal, not something to be worked around.

  1. No getters / setters

Objects do not expose internal state.

Instead, they expose meaningful behavior, such as:

  • is_pending()

  • can_approve()

  • has_rejected()

This prevents external code from bypassing domain rules and keeps invariants protected.

  1. No anemic domain models

Objects are responsible for:

  • their own state

  • their own rules

  • their own state transitions

There are no passive data holders with logic living elsewhere.

Aggregates (like Expense) control their own lifecycle and coordinate with policies and value objects to enforce business rules.

Running tests

On terminal:

  python -m unittest tests/file.py 

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages