Skip to content

Instantly share code, notes, and snippets.

@replaid
Last active March 23, 2023 02:19
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save replaid/e4ea69c1707b30b2d426 to your computer and use it in GitHub Desktop.
Save replaid/e4ea69c1707b30b2d426 to your computer and use it in GitHub Desktop.
A Rails directory tree that expresses DDD layers and bounded contexts.
components/
my_bounded_context/
app/
presentation/ # This is called the "UI" layer in DDD literature, but it is also
# where things like JSON interfaces live -- any kind of presentation
# or handshake to anything outside. So "presentation" rather than "ui".
assets/
helpers/
mailers/
views/
operation/ # Would be named "application" if we weren't already in an "app" directory
controllers/
listeners/ # Wisper plumbing. These should traffic in Domain Events.
services/ # Application services, as distinct from domain services.
# http://gorodinski.com/blog/2012/04/14/services-in-domain-driven-design-ddd/
workers/ # Thin Sidekiq shells that delegate to domain services.
domain/
my_bounded_context/
my_first_aggregate/
repository.rb
my_first_aggregate_root_entity_name.rb
another_entity_in_this_aggregate.rb
an_event_about_this_stuff.rb
value_object_only_in_this_aggregate.rb
a_relevant_factory.rb
a_relevant_service.rb
another_aggregate/
repository.rb
another_aggregate_root_entity_name.rb
second_entity_in_this_other_aggregate.rb
another_event.rb
a_domain_topic_that_is_not_an_aggregate/
a_service_here.rb
perhaps_an_event.rb
and_a_value_object.rb
infrastructure/
persistence/ # ActiveRecords
# This will frequently get broken out as its own component
# that depends on the domain.
my_bounded_context/
whatever_record.rb
whatever_record_repository.rb # gets registered as the provider for the domain repository
# TODO what else in infrastructure? I'm not clear on how anything other than persistence is
# distinguished from application ("operations") logic.
# Perhaps simply promote persistence a level instead?
@jdickey
Copy link

jdickey commented Mar 21, 2015

This is called the "UI" layer in DDD literature, but it is also where things like JSON interfaces live...

In a backend service, the API used to make requests, and the JSON payloads for requests and responses, are the UI for the service. This was a major epiphany for me, too; in 35 years of development, including 15+ years of Web development, that had never occurred to me before.

I think that if I were starting new_poc (or any greenfield app) today, I'd use an architecture that was fundamentally based on Nick Sutterer's Trailblazer architecture. Brownfield apps would get the Growing Rails Applications in Practice treatment. Both were written after years of experience beating their heads against the same problems all Rails teams hit once their app outgrows the 15-minute-blog sweet spot.

My project was started, as a research test-best, back just as Rails 3.2 was passing the torch to 4.0, and used Avdi Grimm's (Rails-3-in-the-details) Objects on Rails book's project as a jumping-off point. I experimented with a couple of different service layers, and (disastrously in retrospect) with Pivotal's unbuilt Rails dependency architectural feature. Were I to write a book about the experience, it would be more than sufficiently similar to the two I've cited for me to simply recommend them enthusiastically.

Hope that helps.


As for your tree above, I'd make a few observations. One of the very, very few things I agree with DHH's stated opinions on is that "good frameworks are extracted from working code". I see enough in this diagram to make me wonder if it's ever actually been implemented.

  1. How do you deal with sharing between aggregates/operations? As you develop out an application and discover significant duplication, how do you support DRYing that up? The major knock I've got against my own present architecture is that pretty much the only way to do that is by an external Gem; unbuilt-dependency Gems for anything smaller than Rails engines are a dead end;
  2. If I have an app built using this architecture sitting on top of Rails, and my CEO walks in and talks about this fantastic mobile opportunity that would require us ripping Rails out in favour of, say, RubyMotion, how does this architecture help or hinder that? I see what would likely have to change spread out across presentation, operation and infrastructure; how traumatic would that change be? I'm assuming you've read "Uncle" Bob Martin's The Clean Architecture post or, ideally, watched his updated presentation (which is a good use of your time IMO).

As a chief engineer of a startup, I want to be able to pivot our applications that delivers business value according to our new definition, not last week's definition. There's no Cucumber plug-in for that, but avoiding code rot, and technical debt, is the single greatest productivity enhancer for you and your team. Getting product out the door is the point, yes?

@replaid
Copy link
Author

replaid commented Mar 21, 2015

@jdickey Many thanks for all of this! I'll definitely check out Trailblazer and Growing Rails Applications in Practice.

I've seen you describing CBRA difficulties in Gitter, too, but am not clear on what specific issues you've encountered with it. I'm using it with a client and it has been working as advertised; both the client and I are pretty enthused so far. I'd be grateful for any details you'd be willing to share. What makes an unbuilt dependency for smaller gems a dead end?

My tree is intended to be a little unrealistic. Its purpose is a sketch pad to rough out a whole set of ideas I have around guiding the organization of the code. There are lots of ideas embedded in here, but the immediate problem I'm solving for is that as I try ideas on for size I might create something called a FooForm or a FooListener or what have you. This client's impulse (as primary developer of the code base) is to go in and arrange that single file into a brand new app/forms or app/listeners directory. I am wanting to illustrate ways to make organizing by concept closer to the first order of organization, with other organization secondary to that. CBRA components are allowing us to realize Bounded Context boundaries in the organization of the code. By introducing the layers of Layered Architecture as the next level of organization, I feel that Ruby modules can serve as DDD Modules and we arrive at an organization that is both logical and useful.

Answering your question #1: code that is shared between aggregates/operations can itself be a peer with those aggregates/operations, or another component—again, interested in what challenges you've found with that approach.

To your question #2: I intend for the domain and infrastructure directories to get promoted to their own components or gems (that lack operation and presentation directories) quite early in the process, much as Ben Smith describes in his CBRA talk. I have trouble imagining such a major transition with anything but the domain surviving—and I envision everything that would be independent of Rails vs. e.g. RubyMotion being modeled somehow in the domain. No?

Yes, I'm pretty solidly in the Uncle Bob school. The challenge I face is finding the best fit for communicating through code to others. Rails provides a hammer and all problems look like nails when looking at the setup it provides. I'm trying to bring the richer DDD etc. concepts to the forefront in the structure of the tree.

@jdickey
Copy link

jdickey commented Mar 21, 2015

@replaid Rails provides a 20kg sledgehammer and we're etching glass. A very neat trick when you can pull that off more than once. The tools, however, do not tilt the odds in your favour. One of my favourite quotes from Nick Sutterer's Trailblazer book is

For every Rails project, there is exactly two outcomes. Either someone in the team’s an experienced architect and leads the software to an advanced design with service layer, view components, maybe forms, and so on. Or, and that’s the classy way, the project strictly follows the Rails Way and will end up as a code disaster.

Having and heeding an architect is no guarantee of avoidance of disaster, but the converse is as reliably true as death and taxes. Or, as I've been putting it half a dozen times over the last two years, when your architect/lead developer/chief engineer puts double-digit percentages of her or his effort into mitigating Rails, let alone my current more than half, you have an existential threat to the success of your project, wrapped beautifully in what you've sold yourself and your other stakeholders as the solution to your project.

I don't think we're in Kansas anymore, Toto. The sky is falling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment