I think it is perfectly possible to write good DDD projects in dynamic languages, but is harder to maintain than in the static ones. Why?
Tooling
With static typed laguages the toolings are usually stronger. Thats why some people are using TypeScript
instead of plain JS
, because it helps you to scale your code by making refactorings easier. Refactoring is something present at everytime when you mainting a DDD code because the business sometimes changes and your knowledge about the model evolves at every day, with this knowledge your code must evolve too. Most of my experience has been with C# and I've built a lot of DDD projects with it. Now I'm working in a DDD project written in Ruby and one of the things I miss the most is the lack of a strong IDE. In Ruby or Python people are used to work using text editors, not IDE's. It's hard to me to see people writing things that some IDE or text editor should be writing for me (i.e. lack of autocomplete). It's hard to see people searching for the full path of a file in Vim just to open it and peek the details of a method or a class - in VS Code or Visual Studio, for example, a single hit on F12
should be enough to go to the definition class or method, without file ambiguity. And I don't even talked about debugging experience, it hurts me to see people writing binding.pry
(for non-ruby developers it is somewhat like a "debugger" keyword in js) in their code just to debug it in the terminal instead of just setting a break point on the line. The list is bigger than this, but I think its enough to make the point about "tooling".
OOP expressiveness
In some dynamic languages like Python and Ruby you don't have all the OOP features like interfaces and abstract classes. That sometimes brings some difficulties in making the code expressive and clear.
Unit testing
You need to write a lot more unit tests to replace what the compiler could do for you.
Dynamic typing
You need to use duck typing if you want to do some kind of type checking. No help from the compiler you get.
Benefits of dynamic typed languages
Safe from Typification Hell
There are always tradeoffs when choosing between OOP dynamic vs static languages.
One common problem in statically typed languages like C# and Java is that sometimes the type system can make the code a lot inexpressive and too verbose. Some developers tends to fall in the generics typification hell. But not all statically typed languages have this problem (F# is one of them - because of the strong type inference).
Testing
Not having static types also helps in some cases when, for example, you don't want to create an interface just to inject to your class and make it testable. In these cases the interface doesn't help in readability, in fact it hurts the readability because you need to create a dumb file (the interface) that doesn't represent anything other than the desire to test the code. In Ruby you could do that in several ways without needing to create an interface, one example would be this:
class DispatchOrderService
def initialize(overrides = {})
@repository = overrides.fetch(:repository) do
::Infra::OrderRepository.new
end
@mail_service = overrides.fetch(:mail_service) do
::Infra::MailService.new
end
end
def dispatch(order)
order.dispatched
repository.save(order)
mail_service.notify_order_dispatched(order)
end
end
Yes, with this approach we broke the clean architecture because the class knows the concrete "infra" implementations. But it is a problem that can be solved with Dependency Injection (in Ruby these frameworks always broke clean architecture too or are too ugly for someone to want to use it, in our projects we did our own DI container by instantiating the dependencies manually on the project startup).
Conclusion
So concluding, I think it is possible to write good "enterprise" DDD applications in Ruby even if it's difficult than in the static languages. My current project is one example of that (60k lines of code and still maintanable).
Also the point mentioned by @GeorgeMaueris is important. You can face problems in implementing DDD in frameworks that imposes on you the way to organize your code. Here we choose to use Hanami instead of Rails because of this, but even Hanami is more "opinionated" that we would like. I really don't recommend anyone to find for "frameworks" to build DDD. The design/architecture changes from application to application and also it evolves. When you choose "DDD" frameworks sometimes you face yourself fighting against it (doing workarounds or monkey patches).
So, maybe you can ask me why we choose ruby at all. The main point to use Ruby here was that almost 100% of the team was composed by Ruby developers and we didn't want to duplicate the difficulties: learning DDD + a new programming language. More a strategic than purely technical decision. The company (a startup) probably wouldn't get so far without it.
EDIT 2018:
We give up from Ruby and Python for our projects that relies in DDD aspects. Now we are using Kotlin and are so much satisfied. The main benefetis were those listed here:
- Best IDE support with IntelliJ. This makes us a lot faster in refactorings, giving erros at compile time and having less efforts writing tests for things that the compiler could do for us.
- Good and popular frameworks for Dependency Injection, ORMs, functional programming
- Another benefits of the language itself (null safety, data classes, etc)
EDIT 2019:
We wrote a series of posts about the move from Ruby to Kotlin. You can see it here.