Search code examples
c#.netdesign-patternsdependency-injectioninversion-of-control

Dependency Injection conceptual problem: where to instantiate new object to save to the db?


I am working on a small toy program to learn some basics about DI and IoC on .Net Core 3.1.

I have several questions, mostly consisting on where and when to perform certain actions.

I have created the following architecture for my project:

A DAL layer, Database First, that has the db entities, context, and extensions for the entities, along with a generic implementation of a repository:

public class Repository<T> : IRepository<T> where T : BaseEntity
{
    private readonly DbContext context;
    private DbSet<T> entities;
    string errorMessage = string.Empty;

    public Repository(DbContext context)
    {
        this.context = context;
        entities = context.Set<T>();
    }

    public IEnumerable<T> GetAll()
    {
        return entities.AsEnumerable();
    }
    public T Get(Guid id)
    {
        //return entities.SingleOrDefault(s => s. == id);
        return null;
    }
    public void Insert(T entity)
    {
        try
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            entities.Add(entity);
            context.SaveChanges();
        }
        catch (Exception e)
        {
            //log ERROR
        }
    }
    public void Update(T entity)
    {
        try
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            context.SaveChanges();
        }
        catch (Exception e)
        {

            //log error
        }
    }
    public void Delete(T entity)
    {
        try
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            entities.Remove(entity);
            context.SaveChanges();
        }
        catch (Exception e)
        {

            //log error
        }
    }

    public void SoftDelete(T entity) {
        try
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            //context.SoftDelete(entity as EntityEntry);
            //context.SaveChanges();
        }
        catch (Exception e)
        {

            //log error
        }
    }
}

I also have a Services and a WebAPI layer, with a monolithic architecture (all services are in the same package, all API requests go against a certain service class within said package).

All real business logic is whitin the Services layer, with the exception of some utilities, exceptions and generic functions in a Commons layer.

Also, I have a "Domain" layer, in which I have the API models. These models are the ones that I use when recieving a POST request, for example.

My biggest question right now is: where/when and how do I create the new instance of the DB entities (from the API Models)?

Right now I have this in my Services layer:

public class AnonymousLoginService : IAnonymousLoginService
{
    public IRepository<AnonymousLogin> _repository { get; set; }

    public AnonymousLoginService(IRepository<AnonymousLogin> repository)
    {
        _repository = repository;
    }


    public void SaveNewLogin(AnonymousLoginModel newEntry)
    {
        //HOW TO DO THIS CORRECTLY?
        var itemToSave = new AnonymousLogin();
        itemToSave.Username = newEntry.Username;
        itemToSave.Id = Guid.NewGuid();
        itemToSave.IsDeleted = false;
        itemToSave.LoginDate = DateTimeOffset.Now.ToString();
        itemToSave.Ipaddress = newEntry.Ipaddress;

        ValidationResult validation = (ValidationResult)itemToSave.ValidateSelf<IAnonymousLogin>();

        if (validation.IsValid)
        {
            _repository.Insert(itemToSave);
        }
        else
            throw new Exception("OMG");
    }


}

AnonymousLoginModel is the object that comes from the WEB Api (the object created from the body of the POST reques) and AnonymousLogin is the entity to be saved to the DB. As you can see, I am creating a new instance of the AnonymousLogin object, but that goes directly against DI. Where should I create the new entry? Is this implementation correct? Should the services layer know both the Web Api models AND the DB objects?

Also, I am worried about the concept of the entire Domain Layer. It it okay that it is in a separate package/project? Should all of those models be inside of the Web Api itself? None of the above?

I would really appreciate the help, since it's very hard to get straigth answers (most information online are examples that don't really apply to this particular scenario). Thanks in advance!


Solution

  • Dependency Injection is the concept of passing depended on objects into the object itself. In this case passing the IRepository<AnonymousLogin> repository into the AnonymousLoginService means that AnonmyousLoginService is dependent upon IRepository<AnonymousLogin>. It has nothing to do with the parameters passed into the methods.

    If you feel it is best to have the separation of concerns and instantiating new db models in the method to verify that it is valid, instead of accepting user input as a db model and passing it in. It is okay.

    Having the Domain Layer in a separate application is not wrong at all. Especially if you don't want to have to create a new Domain Layer for every application built. The Domain Layer could be used this way to loosely couple the code and have one single Domain Layer for multiple applications. Assuming that method return types, method names, and method parameters don't change, a change to the Domain Layer should not break any project depending on it.

    As far as having the api know about both types of models (db and api models) sounds a lot better than having the Domain Layer know about both types of models. The Domain Layer would probably be better served to only know about Domain Models, then every application that uses the Domain Layer could also have access to the Domain Models. Instead of having the Domain Layer understand models from multiple projects, the only thing to consider is how secure the domain models should be? Maybe have set of dummy domain models (public facing db models) and a set of private db models (actual db models) and the Domain Layer gives access to the dummy models and then from there instantiates the actual db models. Ensuring that the actual logic and code is encapsulated.