Home > other >  Best approach to combine repositories logic
Best approach to combine repositories logic

Time:11-24

Let's suppose I have 2 repositories:

  • first is responsible for creating job and return jobId
  • second is responsible for creating log and take jobId as argument

My goal is to:

  • save Job and Log simultaneously
  • prevent situation when in case of error only Job would be saved without Log

What is the most recommended way to get desired result?

I prepared 3 cases which came to my mind but if you see better alternative please share it.

  • option 1 (getting result and save changes in controller)
public class JobRepository : IJobRepository
{
    private readonly Context _context;
    
    public JobRepository(Context context)
    {
        _context = context;
    }

    public Guid CreateJob()
    {
        var job = new Job { Id = Guid.NewGuid() };
        _context.Jobs.Add(job);
        return job.Id;
    }
}
// ...
public class LogRepository : ILogRepository
{
    private readonly Context _context;
    
    public LogRepository(Context context)
    {
        _context = context;
    }

    public void CreateLog(Guid id)
    {
        var log = new Log { Jobid = id };
        _context.Logs.Add(log);
    }
}
// ...
public class JobsController : Controller
{
    private readonly Context _context;
    private readonly IJobRepository _jobRepository;
    private readonly ILogRepository _logRepository;

    public JobsController(Context context, JobRepository jobRepository, ILogRepository logRepository)
    {
        _context = context;
        _jobRepository = jobRepository;
        _logRepository = logRepository
    }

    [HttpGet]
    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Create()
    {
        var id = _jobRepository.CreateJob();
        _logRepository.CreateLog(id);
        _context.SaveChanges();
        return RedirectToAction("Index");
    }
}
  • option 2 (inject one repository into another)
public class JobRepository : IJobRepository
{
    private readonly Context _context;
    private readonly ILogRepository _logRepository;
    
    public JobRepository(Context context, ILogRepository logRepository)
    {
        _context = context;
    }

    public void CreateJob()
    {
        var job = new Job { Id = Guid.NewGuid() };
        _context.Jobs.Add(job);
        _logRepository.CreateLog(job.Id);
        _context.SaveChanges();
    }
}
// ...
public class LogRepository : ILogRepository
{
    private readonly Context _context;
    
    public LogRepository(Context context)
    {
        _context = context;
    }

    public void CreateLog(Guid id)
    {
        var log = new Log { Jobid = id };
        _context.Logs.Add(log);
    }
}
// ...
public class JobsController : Controller
{
    private readonly IJobRepository _jobRepository;

    public JobsController(JobRepository jobRepository)
    {
        _jobRepository = jobRepository;
    }

    [HttpGet]
    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Create()
    {
         _jobRepository.CreateJob();
        return RedirectToAction("Index");
    }
}
  • option 3 (do not use context in controller but declare Save method in each repo)
public class JobRepository : IJobRepository
{
    private readonly Context _context;
    
    public JobRepository(Context context)
    {
        _context = context;
    }

    public Guid CreateJob()
    {
        var job = new Job { Id = Guid.NewGuid() };
        _context.Jobs.Add(job);
        return job.Id;
    }

    public void Save()
    {
        _context.SaveChanges();
    }
}
// ...
public class LogRepository : ILogRepository
{
    private readonly Context _context;
    
    public LogRepository(Context context)
    {
        _context = context;
    }

    public void CreateLog(Guid id)
    {
        var log = new Log { Jobid = id };
        _context.Logs.Add(log);
    }

    public void Save()
    {
        _context.SaveChanges();
    }
}
// ...
public class JobsController : Controller
{
    private readonly IJobRepository _jobRepository;
    private readonly ILogRepository _logRepository;

    public JobsController(JobRepository jobRepository, ILogRepository logRepository)
    {
        _jobRepository = jobRepository;
        _logRepository = logRepository
    }

    [HttpGet]
    public IActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Create()
    {
        var id = _jobRepository.CreateJob();
        _logRepository.CreateLog(id);
        return RedirectToAction("Index");
    }
}

CodePudding user response:

As the use case suggests that the operations (saving and logging) should happen as a single unit of work.

I would suggest an approach similar to the third one. But instead of directly injecting both the repositories into the controller. We could create a service that would then make use of the repositories.

Here we can create a service as follows :

    public class JobService : IJobService
    {
        private readonly IJobRepository _jobRepo;
        private readonly ILogRepository _logRepo;

        public JobRepository(IJobRepository jobRepo, ILogRepository logRepo)
        {
            _jobRepo = jobRepo;
            _logRepo = logRepo;
        }

        public void CreateJob()
        {
            var id = _jobRepo.CreateJob();
            _logRepo.CreateLog(id);
        }
    }

    public class JobsController : Controller
    {
        private readonly IJobService _jobService;

        public JobsController(IJobService jobService)
        {
            _jobService = jobService;
        }

        [HttpGet]
        public IActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Create()
        {
            _jobService.CreateJob();
            return RedirectToAction("Index");
        }
    }

Additional Reading : The Repository-Service pattern

  • Related