Use namespacing for apps and events
We deploy one app to Heroku. But itβs a big Rails app and there are smaller apps within the larger app.
Some examples of these apps:
- Rent estimator
- Search
- Agent finder
- Rehab estimator
- Forum
- Blog
When evolving cross cutting concerns, we think in terms of namespaces.
Problem π
With one global namespace:
- Blast radius for changes can affect many apps at once
- No guidelines on namespaces so codebase can be difficult to navigate
- When we come to splitting up the app, itβll be difficult to know where to start and itβll get harder as we glom more code into our monolithic codebase
- Can devolve into a Big Ball Of Mud
Solution π
Namespacing allows us to:
- Communicate clearly which app an area of code is for
- Precisely identifies features, events so that other departments (i.e. BI) can make sense of data we give them
- Allows for namespacing controllers, models eg:
Marketplace::AgentFinder::Billing::Agent
- Compatible with Domain Driven Design
- Gives flexibility when reporting by using wildcards - i.e. βshow me all events generated by
marketplace.*
β
What is namespacing? π
Describe an entity (event, app, feature, components) by using period separated namespaces.
Examples:
- Event - Rent estimator report generated -
calculators.rent_estimator.reporting.report.generated
- App name - Sitewide search app -
tools.search
- Component - Agent finder featured agents widget -
marketplace.agent_finder.featured_agents
- Feature - Forum show advanced signature -
community.forum.show_advanced_signature
Where do we use namespacing? π
- Classes -
Marketplace::AgentFinder::Searching::Search
- classes wonβt always fit into this exact structure - but where it fits we should use it - Events - useful for domain events we send to Segment. We include the namespace in
context.app.namespace
which allows BI to filter events by app - Components - inside LookBook, we have app specific components in nested directories that mirror this structure where possible
- Features - when defining features, we can define which part of the app they belong to
We have lots of other potential ideas where namespacing could be used over time - UTM tracking, campaign naming, email naming etc.
How do I construct a namespace? π
Event namespaces π
- Area -
marketplace
,calculators
- App name -
agent_finder
,forum
,blog
- Bounded context - optional -
searching
,billing
,leads
- Domain object -
search
,subscription
,lead
,company
- Event action -
requested
,shown
,hidden
,performed
App namespaces π
- Area -
marketplace
,calculators
- App name -
agent_finder
,forum
,blog
Feature namespaces π
- Area -
marketplace
,calculators
- App name -
agent_finder
,forum
,blog
- Feature name -
report_limit
,show_advanced_signature
Component namespaces π
- Area -
marketplace
,calculators
- App name -
agent_finder
,forum
,blog
- Component name -
featured_agents
,latest_news
Concepts explained π
Area π
- An area is often aligned to a team, but not always
- Itβs a region of responsibility
- Examples -
community
,membership
,calculators
,tools
,content
Location analogy - country
App name π
- The name of the app
- Examples -
agent_finder
,forum
,blog
Location analogy - city
Bounded context π
- Bounded context is a concept from Domain Driven Design
- Optional in namespaces - use for more complex apps. For very simple apps, not generally needed.
- Allows us to split complex apps into areas that use the same terms which mean different things depending on the context
Namespacing best practices π
- Technology independent - do not put technology terms in the namespace such as
stripe
,iterable
etc - Instead, add the domain_object such as
invoice
,customer
,email
orcontact
- Ensure the app has the correct area before it
Bad
marketplace.agent_finder.stripe.invoice.paid
- do not put the technology in the event name. This is an implementation detail and can be put into the payload.forum.blog
- apps are nested within specific areas - the blog is part of thecontent
areamarketplace.agent_finder.search_success
- invalid event name - no verb, not in past tense, not clear what this means
Good
marketplace.agent_finder.searching.search.performed
- agent finder is complex, so we break it up into bounded contexts. This is a search being performed within the investor facingsearching
bounded contextcontent.blog.post.published
- the blog is simpler so has no bounded contexts. This namespace reads almost like an English sentence. Bonus marks for readability!