Domain-Driven Design: Tackling Complexity in the Heart of Software

Category: Programming
Author: Eric Evans
4.3
All Stack Overflow 161
This Year Stack Overflow 5
This Month Stack Overflow 15

Comments

by [deleted]   2019-07-21

> I see you have a very little experience working with huge systems.

Huge systems are my bread and butter.

> In a system where you have about 100 000 classes, what you call "small and well contained" is simply impossible.

Not only is it possible, it is necessary. There are excellent books about how to properly design large systems, my favorite being Domain-Driven Design: Tackling Complexity in the Heart of Software.

No well designed large system would have 100,000 classes in one namespace. That screams ball of mud architecture.

Sensible large systems are broken up into modules, with clearly defined responsibilities, with modules divided into layers with clearly defined communication, and small clusters of classes implementing the domain model.

Every huge system that I've worked on that are easy to update and extend have these properties. Nightmarish systems that I have worked on are balls of mud, and when given responsibility for such systems, the first thing I do is start implementing sensible architecture.

by signalling   2019-07-21

I’m fairly certain (sorry if I’m wrong) he means the book by Eric Evans: https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215

by Trinition   2019-07-21

Domain Driven Design by Eric Evans.

by anonymous   2019-07-21

If you use EF directly in business methods (Domain Layer Services & Application Layer Services) then you are not isolating the Domain Model Layer from the infrastructure technologies (EF in this case). That is one of the DDD principles. you should probably have one Repository per Aggregate.

For more info about DDD, see:

Eric Evans' book: http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215

Microsoft: http://msdn.microsoft.com/es-es/architecture/en

by anonymous   2019-07-21

http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215

There is a free version as well at http://www.infoq.com/minibooks/domain-driven-design-quickly

by Scarface74   2019-07-21

Yes the barrier to entry is really low for full stack developers who are good enough for your typical CRUD app and salaries are stagnating. Level up as quickly as possible and learn software engineering.

(None of these are affiliate links)

https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215

https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882

https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164

https://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420

https://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Signature/dp/0134757599

https://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670

https://www.amazon.com/Pragmatic-Programmer-Journeyman-Master/dp/020161622X

by Chibraltar_   2019-07-21

ça

by anonymous   2019-07-21

The generic (and mostly dirty) way of providing hard coded values in Java is to declare static fields or data structures in a class which are then made available by the classloader. This is how a singleton is implemented in most of the cases. But since you delegate the management of creating the single instance to your classloader(s) (there can be multiple classloaders in one VM) there is no guarantee that you will always be using the exact same instance of the static data. So this is not a good recipe to pass around data between multiple threads.

The right way to handle data/content in Android is probably to map your domain model (crosswords) to an implementation using the Android Content Provider API. You can also provide hardcoded values with that if you absolutely need to.

In general: model your domain with apropriate abstract types and not using arrays. That is definitely not the OO way of doing it.

Read this book: http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/ref=sr_1_1?ie=UTF8&qid=1383562013&sr=8-1&keywords=domain+driven+design

by anonymous   2019-07-21

Repository pattern is widely used in DDD (Domain-Driven-Design) you could check it here: http://www.infoq.com/minibooks/domain-driven-design-quickly. Also check this book: http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215

With regards to your question:

Yes you can use more than 1 repository. Look in this example I use nHibernate session:

// crud operations
public abstract class Repository<T> : IRepository<T> where T : class
{
        protected readonly ISession _session;

        public Repository(ISession session)
        {
            _session = session;
        }

        public T Add(T entity)
        {
            _session.BeginTransaction();
            //_session.SaveOrUpdate(entity);
            _session.Save(entity);
            _session.Transaction.Commit();
            return entity;
        }
//...
}

public interface IRepository<T>
{
    T Add(T entity);
    T Update(T entity);
    T SaveOrUpdate(T entity);
    bool Delete(T entity);      
}

Then my repository looks like this:

public class ProjectRepository : Repository<Project>, IProjectRepository
{
 // Project specific operations 
}
public interface IProjectRepository : IRepository<Project>
    {       
        Project Add(Project entity);
        Project Update(Project entity);
        Project find_by_id(int id);
        Project find_by_id_and_user(int id, int user_id);
//..
}

Then using Ninject:

Global.asax.cs

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
            ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
        }

Then in NinjectControllerFactory I load the modules:

 public class NinjectControllerFactory : DefaultControllerFactory
    {

        private IKernel kernel = new StandardKernel(new NhibernateModule(), new RepositoryModule(), new DomainServiceModule());

        protected override IController GetControllerInstance(RequestContext context, Type controllerType)
        {
            //var bindings = kernel.GetBindings(typeof(IUserService));

            if (controllerType == null)
                return null;
            return (IController)kernel.Get(controllerType);
        }
    }

NhibernateModule:

public class NhibernateModule : NinjectModule
    {
        public override void Load()
        {
            string connectionString =            
            ConfigurationManager.ConnectionStrings["sqlite_con"].ConnectionString;

            var helper = new NHibernateHelper(connectionString);

            Bind<ISessionFactory>().ToConstant(helper.SessionFactory).InSingletonScope();
            Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();           

        }
    }

Then in RepositoryModule I use Ninject Conventions to automatically bind all repositories with their interfaces:

using Ninject.Extensions.Conventions;

    public class RepositoryModule : NinjectModule
    {
        public override void Load()
        {
            IKernel ninjectKernel = this.Kernel;

            ninjectKernel.Scan(kernel =>
            {
                kernel.FromAssemblyContaining<ProjectRepository>();                
                kernel.BindWithDefaultConventions();
                kernel.AutoLoadModules();
                kernel.InRequestScope();                

            });

       }
    }

And in the end I basically inject Repository in the controller:

public class projectscontroller : basecontroller { private readonly IProjectRepository _projectRepository;

public projectscontroller(IProjectRepository projectRepository)
{
    _projectRepository = projectRepository;
}

[AcceptVerbs(HttpVerbs.Get)]
[Authorize]
public ActionResult my()
{
    int user_id = (User as CustomPrincipal).user_id;
    var projectList = _projectRepository.find_by_user_order_by_date(user_id);
    var projetsModel = new ProjectListViewModel(projectList);
    return View("my", projetsModel);
}

}

This way you just create new Repository and its Interface and it will be automatically injected to your controller.

by anonymous   2019-07-21

Each of your ways listed in your question is technically "correct" :). Whether you choose one of them or use a different approach depends on the complexity of your use case.

Do read through the DDD bible to understand if you even need a Domain model.

Now let us assume that you do and also further assume that Account is an aggregate root, then you certainly do not want to be exposing your Domain Model to the external word.

You will probably use a DTO or such to collect enough information about the Account model and then delegate to a factory/builder who will build out the Account AR for you.

Again i would encourage you to read through the book and ascertain if you need the complexity.

by raiflip   2019-07-12
Another few books I've found incredibly helpful: Clean Code: https://www.amazon.com/Clean-Code-Handbook-Software-Craftsma... Clean Architecture: https://www.amazon.com/Clean-Architecture-Craftsmans-Softwar... Domain Driven Design: https://www.amazon.com/Domain-Driven-Design-Tackling-Complex...
by cottsak   2019-04-25
Largely agree with this except the initial objection "Data structure and functions should not be bound together" and the "Why was OO popular?" which is missing the response - because using the notion that "objects" from the real world encapsulate state and behaviour was not only a solid premise, but it was an attempt to interface the computer world with the real. This was not a failed concept. It makes some sense. Just very few teams find the discipline to model their core domain this way.

Which brings be back to the original objection. I think this is true most of the time, except when it's not - which is your core Domain Model. The first 1/2 of the Blue Book[1] lays out straightforward means to arrange code, functions, data/state and related behaviours in a way which can be managed and maintained over time. This is pretty important as most folks who've spent any length of time maintaining vast applications will know that it's incredibly hard to reason about a first-class concept in an application without clear boundaries around said concept, it's structures and it's behaviour. Most of us are unlucky and find this scattered across the landscape. Few applications take the focus to "model" these concepts clearly.

Does this modelling have to be done with "Domain Model", or DDD, or something else that can be loosely coupled with OOD - probably not. But another developer absolutely has to be able to reason about said structures and behaviour. They have to be able to read it easily and grok it quickly. And having done that, they don't want to be surprised by some distant missing element, 20 calls or 1000 lines or 15 modules (repos, submodules, etc, etc) away! This is possibly the biggest time-sink and therefore "cost" of development. One could also take this further and postulate that about 1/2 of us are employed as a direct result of applications whose core concepts are so poorly designed or hard to reason about, that a massive volume of work (time?) is dedicated to unwinding the ambiguity that results.

I don't want to suggest that OOP or OOD/DDD/{other modelling process} would necessarily fix this, but the attempt to clarify and find a means to make modelling these critical concepts easier and less costly is admirable IMO.

It's ok if your infrastructure takes a different approach, or is "functional" or "dynamic" in nature. If your test suite uses completely different patterns and paradigms because the tooling makes that easy then - awesome! But if the core model/concepts of your application are hard to understand, reason about, and therefore maintain, then you're pretty fucked.

OO doesn't "suck". It's spirit is just largely lost and like many other things in life, it's been hijacked and mutilated into something many of us come to loathe because we've never seen it deliver on the promises. I guess we will be having this conversation again in another decade about something else that's hugely popular right now.

[1] https://www.amazon.com/Domain-Driven-Design-Tackling-Complex...

by anonymous   2019-01-13

Before you choose an integration mode, there's analysis to be done on a strategic level about the business subdomains, teams that will maintain these services, who owns what, how you expect the models to evolve, etc.

I'd recommend having a look at the Context Mapping part of the DDD book and then Enterprise Integration Patterns by Hohpe and Woolf for concrete implementation.

by scarface74   2019-01-03
(None of these are affiliate links)

https://www.amazon.com/Domain-Driven-Design-Tackling-Complex...

https://www.amazon.com/Clean-Code-Handbook-Software-Craftsma...

https://www.amazon.com/Clean-Architecture-Craftsmans-Softwar...

https://www.amazon.com/Patterns-Enterprise-Application-Archi...

https://www.amazon.com/Refactoring-Improving-Existing-Addiso...

https://www.amazon.com/Code-Complete-Practical-Handbook-Cons...

https://www.amazon.com/Pragmatic-Programmer-Journeyman-Maste...

https://www.amazon.com/Mythical-Man-Month-Software-Engineeri...

And just because it’s asked at every interview.

https://www.amazon.com/Design-Patterns-Object-Oriented-Addis...

I’m focused on AWS these days, but a lot of these principals are universal.

https://d1.awsstatic.com/whitepapers/architecture/AWS_Well-A...

by karmajunkie   2018-11-10
If you're going to dive into CQRS/ES, I'd recommend:

* Enterprise Integration Patterns (basically an entire book about messaging architectures) [1] * Vaughn Vernon's books and online writing [2], * Domain Driven Design by Eric Evans [3], * and most of what Greg Young, Udi Dahan, and that constellation of folks has done online (lots of talks and blog articles.)

Depending on your platform of choice, there may be others worth reading. For my 2¢, the dragons are mostly in the design phase, not the implementation phase. The mechanics of ES are pretty straightforward—there are a few things to look out for, like detection of dropped messages, but they're primarily the risks you see with any distributed system, and you have a collection of tradeoffs to weigh against each other.

In design, however, your boundaries become very important, because you have to live with them for a long time and evolving them takes planning. If you create highly coupled bounded contexts, you're in for a lot of pain over the years you maintain a system. However, if you do a pretty good job with them, there's a lot of benefits completely aside from ES.

[1] https://www.amazon.com/Enterprise-Integration-Patterns-Desig...

[2] https://www.amazon.com/Domain-Driven-Design-Tackling-Complex...

by anonymous   2018-05-09
@AppleBook89 You can read the great tutorial from http://cqrs.nu/ Event-sourcing (and CQRS) works great with (Domain driven design )[https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215]. There are a lot of other sources on the internet, too many in fact to be put here. Look also (here)[https://stackoverflow.com/tags/domain-driven-design/info]
by ChicagoDave   2018-01-25
I can only refer you to Eric Evans book (https://www.amazon.com/Domain-Driven-Design-Tackling-Complex...) and other domain driven design material.

Boundaries are by domain, and yes that's not a simple thing to define. Sometimes, domains have varying interfaces, which makes building micro-services more complex, especially when trying to adhere to REST/Swagger standards (something I'm not overly find of).

But keeping things as simple as possible is really the best approach.

All micro-services should be small. When I see someone say "big", then I'm guessing there are a lot of ad-hoc actions...those need to be broken down into their proper domain or relegated to a query service.

by anonymous   2017-10-01

Implement Repository pattern is pretty easy, you just need to create interface with CRUD methods and use it in your domain logic.

For example:

class CreateEntityException;
class ReadEntityException;
class UpdateEntityException;
class DeleteEntityException;

interface Repository<Entity> {
    Entity create(Entity entity) throws CreateEntityException;
    Entity read(long entityId) throws ReadEntityException;
    Entity update(Entity entity) throws UpdateEntityException;
    void delete(long entityId) throws DeleteEntityException;
}

Methods count and signature can be different in your own project but an approach is the same. After it you can create concrete implementation of repository that encapsulate one or another datasource - ContentProviderRepository, OrmLiteRepository, RealmRepository etc. Then by using Dependency Injection principle you should inject correct implementation.

There are few good books that covered Repository patterns. Pattern is independent from platform so it is easy to implement and use every platform.

https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215

https://www.manning.com/books/functional-and-reactive-domain-modeling

by anonymous   2017-09-18
I agree that this is the best approach. For a very detailed overview of this design pattern, check out the great Eric Evans book, Domain Driven Design (https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215).