What methods should go in my DDD factory class?

后端 未结 7 1397
终归单人心
终归单人心 2021-02-02 17:38

I am struggling to understand what my factory class should do in my DDD project. Yes a factory should be used for creating objects, but what exactly should it be doing. Consid

相关标签:
7条回答
  • 2021-02-02 17:54

    What should go in your factory's Create method is whatever is necessary to put a brand spanking new object into a VALID state.

    Now, for some objects that means you won't do anything except this:

    public Product Create()
    {
       return new Product();
    }
    

    However, you may have business rules, default settings, or other requirements that you want to enforce when an object is created. In that case, you would put that logic in that method.

    And that's part of the benefit of the Factory. You now have one and only one place where that special logic resides, and only one place where a new object gets created.

    0 讨论(0)
  • 2021-02-02 17:56

    I personally would use the factory in couple of circumstances:

    1) Something elsewhere governs what type of objects this factory returns (ie. it can return objects depending on circumstances. For example return a stub object when I am testing, return an actual implementation when I am not (this is obviously more of Inversion of Control / Dependency Injection issue - but if you do not want to add containers to your project just yet)).

    2) I have quite complex objects that have containers, dependencies, other relation etc. and they need to be built carefully to avoid creating null or meaningless references. For example if I have a Schedule object I may need some start, end date fields set - if the logic for retrieving, figuring out these date is complex enough I may not want the calling class to know about it and just call the default factory method that created the schedule object.

    Hope this helps.

    0 讨论(0)
  • 2021-02-02 18:05

    Should i be making a direct call to the repository from within the factory?

    No, don't use a factory when your retrieving stuff, use a factory only when you are creating it for the first time.

    How should i manage object creation when retriving data from a database?

    Pass that data into the factory, if it is required for the object's initial creation.

    What do i need to make this class complete, what other methods should i have?

    Many factories are not even individual classes, they are just methods that provide object creation. You could fold the factory method into another class, if you felt like it was just going to call a parameterless constructor.

    Should i be using this class to create the Product object from the domain and repository from right?

    The repository is for getting (in a sense creating) existing objects, the factory is for the first time you create an object.

    Initially many factories won't do much except call a constructor. But once you start refactoring and/or creating larger object hierarchies, factories become more relevant.

    Explanation and Example:

    For instance, in the project I'm working on I have an excel processor base class and many subclasses implementing that base class. I use the factory to get the proper one, and then call methods on it, ignorant of which subclass was returned.(Note: I changed some variable names and gutted/altered a lot of code)

    Processor base class:

    public abstract class ExcelProcessor
    {
          public abstract Result Process(string ExcelFile);
    }
    

    One of the Processor subclasses:

    public class CompanyAExcelProcessor : ExcelProcessor
    {
         public override Result Process(string ExcelFile)
         {
          //cool stuff
         }
    }
    

    Factory:

     public static ExcelProcessor CreateExcelProcessor(int CompanyId, int CurrentUserId)
     {
          CompanyEnum company = GetCompanyEnum(CompanyId);
          switch (company)
          {
               case CompanyEnum.CompanyA:
                    return new CompanyAExcelProcessor();
               case CompanyEnum.CompanyB:
                    return new CompanyBExcelProcessor();
               case CompanyEnum.CompanyC:
                    return new CompanyCExcelProcessor(CurrentUserId);
               //etc...
          }
     }
    

    Usage:

    ExcelProcessor processor = CreateExcelProcessor(12, 34);
    processor.Process();
    
    0 讨论(0)
  • 2021-02-02 18:05

    @ThinkBeforeCoding - in @m4bwav's example, the factory is getting a valid ID from a helper method, but it's not creating a new record in a persistence layer anywhere. If, however, I'm using a database auto-generated identity column as my identities, it seems like a factory would have to call into the repository to do the initial object creation. Can you comment on which method is "correct"?

    0 讨论(0)
  • 2021-02-02 18:10

    In the example given above, I'm a little unclear on the distinction between your factory and the repository. I wonder if you shouldn't simply add CreateProduct as a method to the repository, and using DI to push the repository into code that needs it? If the factory isn't doing anything, etc...

    Or if you just want it to act as a globally registered repository, perhaps something like:

    public static IFooRepository Default {get;private set;}
    public static void SetRepository(IFooRepository repository) {
        Default = repository;
    }
    

    (in my mind it seems clearer to separate the "set" in this case, but you don't have to agree)

    and have the callers use var product = YourFactory.Default.CreateProduct(); etc

    0 讨论(0)
  • 2021-02-02 18:15

    In the builder you can have any logic you need to inforce the invariants on your entites, a little example using Java as development language...

    I have a User entity that has a username, a password and an email, all attributes required so I have:

    public class User {
        private String username;
        private String password;
        private String email:
    
        /**
         * @throws IllegalArgumentException if the username is null, the password is null or the
         * email is null.
         */
        public User(final String theUsername, final String thePassword, final String theEmail) {
            Validate.notNull(theUsername);
            Validate.notNull(thePassword);
            Validate.notNull(theEmail);
    
            this.username = theUsername;
            this.password = thePassword;
            this.email = theEmail;
        }
    
        // Getters / Setters / equal / hashCode / toString
    }
    

    and then I have the UserBuilder:

    public class UserBuilder {
        private String username;
        private String password;
        private String email;
    
        public UserBuilder withUsername(final String theUsername) {
            Validate.notNull(theUsername);
    
            this.username = theUsername;
    
            return this;
        }
    
        public UserBuilder withPassword(final String thePassword) {
            Validate.notNull(thePassword);
    
            this.password = thePassword;
    
            return this;
        }
    
        public UserBuilder withEmail(final String theEmail) {
            Validate.notNull(theEmail);
    
            this.email = theEmail;
    
            return this;
        }
    
        public User build() {
            User user = new User(this.username, this.password, this.email);
    
            return user;
        }
    }
    

    And you can use the builder like this:

    UserBuilder builder = new UserBuilder();
    
    try {
        User user = builder.withUsername("pmviva").withPassword("My Nifty Password").withEmail("pmviva@somehost.com").build();
    } catch (IllegalArgument exception) {
        // Tried to create the user with invalid arguments
    }
    

    The factory's solely purpose is th create valid instances of objects. In order not to duplicate creation and hydration code you can have your repositories to query a rowset from the database and delegate the creation of the object to a builder passing the rowset's data.

    Hope this helps

    Thanks Pablo

    0 讨论(0)
提交回复
热议问题