ASP.NET MVC podejście programistyczne

0

Witam, programuje w MVC i chciałbym zapytać was o prawidłowe podejście w mojej aplikacji!!!

Model aplikacji utworzyłem za pomocą Code First przykładowy model

 
 public class Category
    {
        public int CategoryID { get; set; }
        public string CategoryName { get; set; }
        public bool Status { get; set; }
        public bool Visible { get; set; }

        public virtual ICollection<Product> product { get; set; }
}

do tego dodałem Repozytoria, które odpowiadają za operacje na bazie danych.

    public class CategoryRepository : GenericRepository<Category>
    {
        public CategoryRepository(AppDbContext context) : base(context) { }

        public virtual Category GetObjectById(int id)
        {
            return dbSet.Where(p => p.CategoryID == id).First();
        }
    }
 

Repozytoria umiejscowiłem w klasie unityofwork, która wygląda następująco.

 
 public class UoW : IDisposable, IUow
    {
        private bool disposed = false;

        private AppDbContext context = new AppDbContext();
        //public ICategory _category;
        private ProductRepository _productRepository;
        private CategoryRepository _categoryRepository;
        private ImageRepository _imageRepository;

        public ImageRepository ImageRepository
        {
            get
            {

                if (this._imageRepository == null)
                {
                    this._imageRepository = new ImageRepository(context);
                }
                return _imageRepository;
            }
            set { _imageRepository = value; }
        }
          .
          .
          .
    }

Jednak posiadam jeszcze klasy, które mają "reprezentować logikę biznesową" tj. CategoryService, ProductService itd.

 
  public class CategoryService : ICategory
    {
        private UoW _uow;


        public CategoryService(UoW uow)
        {
            this._uow = uow;
        }

        public async Task<Result> CreateCategory(Category category)
        {
            try
            {

                _uow.CategoryRepository.Create(category);             

                return Result.Success;

            }catch (Exception ex)
            {
                return Result.Failed;
            }
            finally
            {
                // wykona się niezależnie, czy pojawi się wyjątek, czy też nie!
            }

        }

        public async Task<Result> UpdateCategory(Category category)
        {
            try
            {

                _uow.CategoryRepository.Update(category);

                return Result.Success;

            }
            catch (Exception ex)
            {
                return Result.Failed;
            }
            finally
            {
                // wykona się niezależnie, czy pojawi się wyjątek, czy też nie!
            }

        }
    public List<Category> GetActiveCategories()
        {
            List<Category> list = null;
           
            try
            {
                list = _uow.CategoryRepository.GetAll();

                if(list != null)
                {
                    foreach (Category item in list.ToList())
                    {
                        if (item.Status == false)
                        {
                            list.Remove(item);
                        }
                    }

                    
                }

                return list;
            }
            catch (Exception ex)
            {

            }
            return list;
        }
}

Klasy kontrolerów:

public class CategoryController : Controller
    {
        private ICategory _categoryService;


        public CategoryController(ICategory category)
        {
            this._categoryService = category;
        }

        [AllowAnonymous]
        public PartialViewResult ListMenu()
        {
            List<Category> list = _categoryService.GetActiveCategories();
            return PartialView(list.ToList());
        }

  [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Create(CategoryViewModel modelView)
        {
            if (ModelState.IsValid)
            {
                Category category = Mapper.Map<CategoryViewModel, Category>(modelView);

                var exist = await _categoryService.IsExist(modelView.CategoryName);
                   
                   if(exist)
                   {
                       ModelState.AddModelError("", "Taka kategoria już istnieje!");
                       return PartialView(modelView);
                   }

                   var result = await _categoryService.CreateCategory(category);
                  
                  switch(result)
                  {
                      case Result.Success:
                          _categoryService.Save();

                          return RedirectToAction("Index", "Category");
                      case Result.Failed:
                          return PartialView("Error");
                      
                  }                                             
            }

            if (Request.IsAjaxRequest())
                return PartialView(modelView);

            return View(modelView);
        }
}
 

Tutaj zaczynaja się moje pytania.

  1. Czy klasy serwisowe powinny posiadać takie metody jak Create, udpate, delete? i to w nich powinna znajdować się klasa UnityOfWork z dostępem do repozytoriów?
  2. Jeżeli NIE to jakie metody interpretują logikę biznesową!! (prosiłbym o jakieś przykłady!
  3. Czy może klasa UoW powinna być wywoływana w kontrolerze i operować na repo i serwisach?

Mam ogólny chaos ze zrozumieniem metod logiki biznesowej. Czy np. metoda IsExist powinna znajdować się w Serwisach czy po prostu, weryfikować to na podstawie repozytoriów. Liczę na waszą pomoc, chciałbym nauczyć się porządku wraz z wzorcami.

Pozdrawiam.

0

Twój problem nie ma żadnego związku z MVC, a z projektowaniem aplikacji wielowarstwowych.

Kontrolery powinny być cienkie i nie wykonywać żadnej logiki biznesowej, więc Twój kontroler nie jest dobry - bo najpierw sprawdza, czy kategoria istnieje, a potem ją tworzy. To sprawdzenie powinno być wewnątrz metody CreateCategory, ona też powinna zwrócić informację o powodzeniu/niepowodzeniu operacji, wraz z kodem/opisem błędu.

Kontroler powinien korzystać z serwisu aplikacyjnego, który to może korzystać z serwisów biznesowych (bo w realnym świecie pojedyncze operacje wymagają wykorzystania wielu encji) opakowując je w jeden UoW. Serwisy biznesowe niech mają referencje do UoW i repozytoriów, z których korzystają.
Ponadto to repozytoria powinny przyjmować UoW jako referencję, nie odwrotnie. Nie wiem skąd ta moda na trzymanie repozytoriów w UoW, ale niczego to nie upraszcza, a więc przeciwnie - bo dodając nowe repozytorium trzeba zmieniać implementację UoW, łamiąc przy tym SRP, OCP i Konwencję Genewską.

Tyle, jeśli chcesz mieć aplikację zgodną z podejściem DDD. Tymczasem, w przypadku prostych CRUDów jest to kompletny overkill, bo tak naprawdę, to masz trzy warstwy wrapperów łączące kontroler z ORMem. W takiej sytuacji można sobie uprościć życie korzystając z ORMa w serwisie aplikacyjnym. Wszak obiekt ORMa jest już i repozytorium generycznym, i implementacją UoW.

1 użytkowników online, w tym zalogowanych: 0, gości: 1