paulrayner / Ddd_sample_app_ruby
Programming Languages
== Ruby DDD Sample App
=== Goal
The goal of this sample app is to provide an idiomatic Ruby port of the DDD sample application. It should remain faithful to the intention of the purposes of the original DDD sample application.
=== Why a Ruby port?
Because Paul wanted to learn Ruby and see how well it supports DDD. Also, interested in questions like:
- How does the choice of Ruby affect the implementation of the DDD building block patterns?
- How well does an opinionated MVC framework like Rails support doing DDD?
- What are implications of choosing a document store like MongoDB for aggregate design and eventual consistency?
=== Why a DDD Sample App?
- Provide a how-to example for implementing a typical DDD application
- Descriptive, not prescriptive
- Idiomatic
- Update sample app with latest ideas
- Support discussion of implementation practices
- Engage Ruby community in dialog and learning about DDD
- Show design and implementation tradeoffs
- Teach DDD (tend to be Java & .NET) community more about Ruby
- Lab mouse for controlled experiments
- Learn Ruby as a language and ecosystem
- Can Rails + MongoDB carry the weight of a complex domain model?
- Tradeoffs between Sinatra vs Rails for Ruby web apps
=== Problems with Sample Apps
- http://lostechies.com/jimmybogard/2008/10/22/where-are-the-ddd-sample-applications/[Against DDD sample applications] - Wise words from Jimmy Bogard
=== DDD Sample Application
- link:https://github.com/patrikfr/dddsample[Written in Java in 2009].
History
- Sep 2008 First public release: 1.0.
- Jan 2009 Sample application tutorial at the JFokus conference in Stockholm.
- Mar 2009 Sample application tutorial at the QCon conference in London.
- Mar 2009 New public release: 1.1.0. See changelog for details.
- 2010/2011 https://github.com/SzymonPobiega/DDDSample.Net[Ported to .NET in several flavors].
- May 8, 2013 Begin porting to Ruby at https://github.com/paulrayner/ddd_sample_app_ruby
- May 13, 2013 Presentation of early work on port of sample app to Ruby at DDD Denver. Slides are http://virtual-genius.com/presentations/ddd_with_ruby_20130613.html[available online].
The .NET port is being used as the primary basis for this Ruby port.
== Implementation Stack
- Persistence: MongoDB
- Data access: Mongoid-backed repositories
- Domain model: Plain Ruby objects
- UI: Rails stack (w/ Twitter bootstrap)
- Aggregate eventual consistency: Wisper-async leveraging Celluloid
- GOAL: Before DDDx London - June 14 DONE!
_The focus of this version is to see how implementing a domain model within Rails affects the implementation.
== Aggregates
The aggregate roots are:
- Cargo
- HandlingEvent
- Location
- Voyage
== Design Decisions
Here you'll find information on design choices made, and the relative tradeoffs. Plus resources for further reading. Actually, mostly resources right now.
=== Value Objects
==== Immutability in Ruby
-
link:https://deveo.com/blog/2013/03/22/immutability-in-ruby-part-1/[Immutability in Ruby - part 1 of 2]
-
link:https://deveo.com/blog/2013/03/28/immutability-in-ruby-part-2/[Entities and value objects in Ruby - part 2 of 2]
-
http://voormedia.com/blog/2013/02/creating-immutable-tree-data-structures-in-ruby[Creating immutable tree data structures in Ruby - Feb 2013]
-
http://www.confreaks.com/videos/2337-mwrc2013-immutable-ruby[Immutable Ruby presentation video (25 mins) - Michael Fairley @ MountainWest RubyConf 2013]
-
http://blog.rubybestpractices.com/posts/rklemme/017-Struct.html[Ruby structs inside-out]
-
http://functionalruby.com/blog/2012/02/23/hamster-immutable-data-structures-for-ruby[Blog post on Functional Ruby blog about Hamster]
-
http://www.harukizaemon.com/blog/2010/03/01/functional-programming-in-object-oriented-languages/[Functional programming in object oriented languages] - Blog post by Simon Harris, author of Hamster.
==== Libraries/Gems Supporting Immutability in Ruby
- https://rubygems.org/gems/ice_nine[Ice Nine (for deep freezing objects)]
- https://github.com/harukizaemon/hamster[Hamster - Efficient, Immutable, Thread-Safe Collection classes for Ruby]
- https://github.com/tcrayford/values
- https://github.com/solnic/virtus
- https://github.com/hdgarrood/value_object
- https://github.com/rouge-lang/rouge[Ruby + Clojure = Rouge]
=== Enums in Ruby
- http://stackoverflow.com/questions/75759/enums-in-ruby
- http://www.lesismore.co.za/rubyenums.html
- http://gistflow.com/posts/682-ruby-enums-approaches
=== Equality in Ruby
- http://woss.name/2011/01/20/equality-comparison-and-ordering-in-ruby/[Equality, Comparison and Uniqueness in Ruby]
- http://stackoverflow.com/questions/11247000/which-equality-test-does-rubys-hash-use-when-comparing-keys[SO: Which equality test does Ruby's Hash use when comparing keys?]
- http://pivotallabs.com/equality-and-sameness-in-ruby/[Equality and sameness in RubyConf]
- http://kentreis.wordpress.com/2007/02/08/identity-and-equality-in-ruby-and-smalltalk/[Identity and Equality in Ruby and Smalltalk]
== Persistence
=== MongoDB
- link:http://speakerdeck.com/u/mongodb/p/domain-driven-design-with-mongodb-chris-hafey-on-point-medical-diagnostics[Presentation on Domain Driven Design with MongoDB]
- link:http://wiki.basho.com/Riak-Compared-to-MongoDB.html[Riak Compared to MongoDB]
- https://github.com/basho/ripple/wiki[Ripple is a rich Ruby client for Riak, Basho’s distributed database]
- http://docs.mongodb.org/ecosystem/drivers/ruby/[Mongo Ruby driver]
==== Mongo ORMs
-
http://mongoid.org/en/mongoid/index.html[Mongoid] - Object-Document-Mapper (ODM) for MongoDB written in Ruby. Has Echo sample app - take a look at
application.rb
- it's using Sidekiq and Kiqstand (not sure what for...maybe could be used for aggregate updates?) - https://github.com/mongomatic/mongomatic[Mongomatic] - A MongoDB super-set that adds nice features over the traditional Ruby Driver. Map your Ruby objects to Mongo documents. It is designed to be fast and simple.
- http://mongomapper.com/[MongoMapper] - ODM for MongoDB written in Ruby.
=== Repository Pattern in Ruby
- http://mattbriggs.net/blog/2012/02/23/repository-pattern-in-ruby/
- https://github.com/nfedyashev/repository[A Ruby implementation of the Repository Pattern - In memory only], developed from https://github.com/alexch/treasury[Repository Pattern for Ruby - 3 years old].
- https://github.com/playlouder/persistence[A set of interfaces for, and implementations of, the Repository pattern in Ruby.] This one looks promising.
- https://github.com/brandonweiss/collector[Collector is an implementation of the Repository Pattern for MongoDB]
- https://github.com/braintree/curator[Curator is a model and repository framework for Ruby].Currently, curator supports Riak, MongoDB and an in-memory data store for persistence.
- https://github.com/braintree/curator_rails_example[Curator Rails example]
- http://www.pgrs.net/2012/02/21/untangle-domain-and-persistence-logic-with-curator[Good blog post by Paul Gross: "Untangle Domain and Persistence Logic with Curator"]
- http://www.pgrs.net/2012/03/08/data-migrations-for-nosql-with-curator/[Data migrations for NoSQL with Curator]. "Curator migrations are lazy, so at any given time you might have documents with different versions in the data store."
- https://gist.github.com/bokmann/2217602[ActiveRepository "Strawman" gist by David Bock]. Proposal for what a good Repository pattern implementation should look like in Ruby. Comment thread is excellent value.
- http://datamapper.org/[DataMapper 2] - goal is to create an ORM which is fast, thread-safe and feature rich. Last release was 1.2, but active development on v2 seems to be progressing.
- https://github.com/fredwu/datamappify[Datamappify] - is built using Virtus and existing ORMs (ActiveRecord and Sequel, etc). Compose and manage domain logic and data persistence separately and intelligently, Datamappify is loosely based on the Repository Pattern and Entity Aggregation. Datamappify is current in Proof-of-Concept stage, do NOT use it for anything other than experimentation.
Have not yet found a repository implementation that supports aggregates. Rather, each implementation follows a repository-per-object approach, which is not what we need.
There is an on issue for Curator regarding https://github.com/braintree/curator/issues/16[ supporting foreign keys and embedded objects], and some experimentation in a branch with adding a https://github.com/braintree/curator/commit/repository_mapping[mapping API] which may do what I need.
https://github.com/ifesdjeen/entrepot[Entrepot] looks promising. It uses Virtus for the objects and has this kinda weird approach of referencing a repository from a repository:
[source, ruby]
class Address include Virtus include Entrepot::Model
attribute :street, String attribute :city, String attribute :country, String end
class Person include Virtus include Entrepot::Mongo::Model
attribute :name, String attribute :address, Address end
class PersonRepository include Entrepot::Repository
has_many :articles, :repository => :ArticleRepository end
=== Aggregates
==== Concurrency in Ruby
- http://www.slideshare.net/ThoughtWorks0ffshore/concurrency-patterns-in-ruby-3547211[Concurrency patterns in Ruby - Thoughtworks presentation]
- https://github.com/tenderlove/tusk[Message busses with Observable API]
- http://www.slideshare.net/KyleDrake/hybrid-concurrency-patterns[Presentation on NOT using Eventmachine], advocates Celluloid
-
http://blog.paracode.com/2012/09/07/pragmatic-concurrency-with-ruby/[Pragmatic Concurrency With Ruby] - great article, which also discusses how Celluloid uses
mutex
to thread-safe its mailboxes.
==== Eventual Consistency
Resources for implementing eventual consistency (i.e. performing asynchronous updates) between aggregate instances.
===== Worker Queues
- http://rubylearning.com/blog/2010/11/08/do-you-know-resque[Learning Resque]
- http://railscasts.com/episodes/271-resque[Railscast on Resque]
- https://devcenter.heroku.com/articles/queuing-ruby-resque[Queuing in Ruby with Redis and Resque - Heroku Blog]
- https://github.com/nesquena/backburner[Simple and reliable beanstalkd job queue for ruby]
- https://github.com/iron-io/delayed_job_ironmq[IronMQ backend for delayed_job]
- https://github.com/mperham/sidekiq[Sidekiq] - Simple, efficient message processing for Ruby, based on Celluloid actor model
- http://railscasts.com/episodes/366-sidekiq[Railscast on Sidekiq]
===== Messaging
- http://rubyamqp.info/articles/getting_started/[Ampq/RabbitMQ]
- http://www.iron.io/[IronMQ is the Message Queue for the Cloud], see http://www.iron.io/mq[comparison chart]
- http://rubysource.com/an-introduction-to-celluloid-part-ii/
===== Celluloid
- http://www.unlimitednovelty.com/2011/05/introducing-celluloid-concurrent-object.html["Introducing Celluloid: a concurrent object framework for Ruby" - Blog post from May 11. 2011]
- https://groups.google.com/forum/?fromgroups#!forum/celluloid-ruby[Celluloid Google Group]
- http://www.confreaks.com/videos/1302-rubyconf2012-the-celluloid-ecosystem[RubyConf presentation on Celluloid by Tony Arcieri]
- http://rubysource.com/an-introduction-to-celluloid-part-i[An Introduction to Celluloid - Part II] and http://rubysource.com/an-introduction-to-celluloid-part-ii/[An Introduction to Celluloid - Part II]
- http://railscasts.com/episodes/367-celluloid[Railscast (pro) on Celluloid] - good examples
=== DDD and Rails
- http://victorsavkin.com/post/41016739721/building-rich-domain-models-in-rails-separating[Entity Data Repository] - Blog post describing hybrid ActiveRecord/DAO approach to building rich domain models in Rails, implemented in https://github.com/nulogy/edr[EDR library]. Implements restricted version of http://martinfowler.com/eaaCatalog/dataMapper.html[DataMapper pattern]. Datamapper 2 will be implementing the same pattern, but is not production-ready yet (see above)
- http://iain.nl/domain-driven-design-building-blocks-in-ruby[DDD in Ruby article] - recommends using to_s for UI concerns and structs for value objects, both of which seem problematic to me.
- https://github.com/cavalle/banksimplistic[Interesting implementation of CQRS in Rails with Redis]
- http://blog.carbonfive.com/2012/01/10/does-my-rails-app-need-a-service-layer/[Does My Rails App Need a Service Layer?] - blog post from Jan 2012 by Jared Carroll
- http://confreaks.com/videos/977-goruco2012-hexagonal-rails[Hexagonal Rails] - Video of Matt Wynne's Goruco 2012 presentation
- https://www.agileplannerapp.com/blog/building-agile-planner/refactoring-with-hexagonal-rails[Refactoring with Hexagonal Rails] - blog post showing how to set up pub/sub eventing for use within Rails (inspired by Matt Wynne's approach of passing controller object into domain object, so domain object can run a success/failure callback method on the controller)
- https://github.com/krisleech/wisper[Wisper] - Ruby library for decoupling and managing the dependencies of your domain models]. See also this http://shcatula.wordpress.com/2013/06/02/whisper-ruby/[blog post on Wisper] and this https://gist.github.com/krisleech/5326823[business case Gist].
- https://github.com/krisleech/wisper-async[Wisper-Async] - Extends Wisper with async broadcasting of events. Each listener is transparently turned in to a Celluloid Actor.
== Contributing
This is a learning experiment, pull requests are welcome! Bonus points for feature branches.
To get started, see https://github.com/paulrayner/ddd_sample_app_ruby/issues?state=open[milestones and issues]. Use the https://github.com/SzymonPobiega/DDDSample.Net[vanilla .NET port version] as the basis for any work.
Progress and learning will be shared after the DDD Exchange on June 14 through posts on http://thepaulrayner.com[Paul Rayner's blog].
== Copyright
Copyright (C) 2013 Paul Rayner. See link:LICENSE[LICENSE] for details.