1

I am wondering how to deal with a situation when inside one service lets say ICompanyService I need to call another method from IUserAccountService. ?

So generally lets say that a Company shouldn't exist without an UserAccount.

The IUserAccount implementation service class looks like this:

public class UserAccountService : CrudService<UserAccount>, IUserAccountService
{
    private readonly IRepository<UserAccount> _userAccountRepository;
    private readonly IUnitOfWorkFactory _unitOfWorkFactory;

    public CompanyService(IRepository<UserAccount> userAccountRepository,
                          IUnitOfWorkFactory unitOfWorkFactory)
        : base(userAccountRepository, unitOfWorkFactory)
    {
        _userAccRepository = userAccRepository;
    }

    public int RegisterUser(UserAccount user) {

        using (var uow=_unitOfWorkFactory.Create())
        {
           // Details omitted for brievity
           var userId = _userAccountRepository.Create(user);
           uow.Commit();
           return userId;
        }
    }

   //Other service methods

}

The company ICompanyService implementation:

public class CompanyService : CrudService<Company>, ICompanyService
{
    private readonly IRepository<Company> _companyRepository;
    private readonly IUnitOfWorkFactory _unitOfWorkFactory;

    public CompanyService(IRepository<Company> companyRepository,
                          IUnitOfWorkFactory unitOfWorkFactory)
        : base(companyRepository, unitOfWorkFactory)
    {
        _companyRepository= companyRepository;
    }

    public int CreateCompanyWithUserAccount(Company company) {

        using (var uow=_unitOfWorkFactory.Create())
        {
           // Some validation with the company.Details omitted for brievity
           // Here I need an instance of IUserAccountService
           // Suppose I get it through DI or IoC
           var userAccountService = IoC.Resolve<IUserAccountService>();
    ###    // Is such approach good or bad?!    ###
           var userId = userAccountService.RegisterUser(company.UserAccount);
           // Map the user id to the company
           company.UserAccount.Id = userId;
           var companyId = _companyRepository.Create(company);
           uow.Commit();
           return companyId;
        }
    }

   //Other service methods

}

ORM under the repository is: NHibernate

Cristian E.
  • 3,116
  • 7
  • 31
  • 61

2 Answers2

0

You could just take a dependency on the IRepository<UserAccount>:

public class CompanyService : CrudService<Company>, ICompanyService
{
    private readonly IRepository<Company> _companyRepository;
    private readonly IRepository<UserAccount> _userAccountRepository;
    private readonly IUnitOfWorkFactory _unitOfWorkFactory;

    public CompanyService(IRepository<Company> companyRepository,
                          IUnitOfWorkFactory unitOfWorkFactory
                          IRepository<UserAccount> userAccountRepository)
        : base(companyRepository, unitOfWorkFactory)
    {
        _companyRepository= companyRepository;
        _userAccountRepository = userAccountRepository;
    }

    public int CreateCompanyWithUserAccount(Company company) {

        using (var uow=_unitOfWorkFactory.Create())
        {
           // Some validation with the company.Details omitted for brievity
           var userId = _userAccountRepository.Create(company.UserAccount);
           // Map the user id to the company
           company.UserAccount.Id = userId;
           var companyId = _companyRepository.Create(company);
           uow.Commit();
           return companyId;
        }
    }

   //Other service methods

}

IMO, it's better to take a dependency on the repository. After all your company service is creating a company and it needs to do some work in the database, which is what the repositories are for. From what I can see in the code, there's no need to involve the UserAccountService.

Kenneth
  • 28,294
  • 6
  • 61
  • 84
  • 1
    Would then be one unit of work instance inside another? I am not sure if it is good or bad – Cristian E. Feb 06 '14 at 11:36
  • Supoosing you're only querying the companyservice there should be no problem. And if you're only querying, why not take a dependency on `IRepository` instead of on the service? Supposedly, the logic you want to implement has more to do with UserAccount then with companies. – Kenneth Feb 06 '14 at 11:38
  • Please take a look at the ICompanyService implementation – Cristian E. Feb 06 '14 at 11:49
  • You should probably take a dependency on an `IRepository` inside your `CompanyService` and do the insert there. – Kenneth Feb 06 '14 at 11:57
  • Edited my answer to reflect this – Kenneth Feb 06 '14 at 12:01
  • The `RegisterUser(UserAccount user)` method does different sort of validation rules (i've ommitted for brievity). So doing a copy-paste of it, inside `CreateCompanyWithUserAccount(Company company)` would break the DRY principle isn't it? – Cristian E. Feb 06 '14 at 12:06
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/46929/discussion-between-kenneth-and-christian) – Kenneth Feb 06 '14 at 12:11
0
  1. Seems you have wrong constructor in UserAccountService implementations: public CompanyService

  2. In CompanyService implementation, you better resolve IUserAccountService dependency right in a constructor, so you do it once per object creation, not each time you call method.

  3. There's no problems with those dependencies. If two objects of IUnitOfWorkFactory implementations are problem -> make a singleton

Sergio
  • 6,900
  • 5
  • 31
  • 55
  • I agree with you that injecting `IUserAccountService` inside `CompanyService`'s constructor is a much better approach than `Service Locator`, but, what about the fact that calling `RegisterUser(UserAccount user)` inside `CreateCompanyWithUserAccount(Company company)` may need be rolled back if `CreateCompanyWithUserAccount` fails? But in that case there is no roll back scenario. It remains to `DeleteUser(userId)` or what? – Cristian E. Feb 06 '14 at 12:01
  • More literally would sound like this: While registering a Company, I register an user account for it, so the nested transaction is already commited. But what happens if the company registration fails after the creation of it's user account (which is already commited)? Should I delete it ? – Cristian E. Feb 06 '14 at 12:09
  • @Christian If you are on a good terms with SQL you should do it in 1 transaction. And a repository method call should look like `bool CreateUserAccountWithCompany`. – Sergio Feb 06 '14 at 12:21
  • @Christian or you need another method `DeleteUser` wich aint so good as both actions in a single sql(or w/e you have on data layer) transaction – Sergio Feb 06 '14 at 12:22
  • Sound's little weird `CreateUserAccountWithCompany` maybe you meant `CreateCompanyWithUserAccount`. I think that Company's UserAccount is more appropiate than UserAccount's Company. Many Entities may have UserAccounts, lets say `Employee`, `Supplier`, `Company` etc. So I'd rather put an UserAccountId to them instead of messing the UserAccount table with nullable EmployeeId, SupplierId, CompanyId etc – Cristian E. Feb 06 '14 at 12:31
  • @Christian Doesn't matter for me, depends on business logic – Sergio Feb 06 '14 at 12:35
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/46932/discussion-between-christian-and-sergio) – Cristian E. Feb 06 '14 at 12:48