I am in the process of making project decisions on development patterns for a solution, which involves the use of Entity Framework 6 as the ORM choice, and ASP.NET MVC 5.
I need insight on how transactions and business logic will be implemented. In respect to layers, I came to an initial assumption for the design where Entity Framework on top of SQL Server can be considered the Data Access Layer (DAL). On top of Entity Framework, there will be a Service Layer, where business logic and validation will be implemented. On top of the Service Layer, I will have ASP.NET MVC Controllers consuming what the service layer offers.
Let me ellaborate on this initial conclusion drawn as a starting point for defining the architecture:
I want to follow principles to achieve the minimum complexity scenario possible, in respect to layers, abstractions and all the solution components responsibilities. As an excercise, with this "simplicity" in mind, I could just embrace the template "proposed" by Microsoft as when you just create a new Visual Studio ASP.NET MVC Web application, but I believe that does not fit the minimum design scenario needed for an enterprise application, since in Microsoft's template, the controller directly makes use of Entity Framework DbContext and consumes the Data Access Layer, besides the fact that no service layer is present. This leads to several issues, such as extremely tight coupling between the presentation and data access layer, as well as the so called "fat controller" problem, where controllers become the bloated piece of the software with all the added responsibilities of business logic, transactions, and so on, making it truly a mess to software maintainability with, for example, the most basic principle of DRY (don't repeat yourself) being violated since you would get duplicated code and logic all over your fat controllers.
Ramping up the next stage on the path from simplicity to complexity, I assume it is fair to add a Service Layer to the design, because this way ASP.NET MVC controllers would talk only to this service layer, who would be responsible for all CRUD and validation of CRUD operations, and all other more complex business logic operations. This Service Layer then would talk to the data access layer being represented by Entity Framework.
I would stop there and say the design with these proposed layers is enough, but that's where I need more insight on how to proceed. I need to resolve the question on how would transactions be implemented, if you think of them as a wrapper for a series of individual operations performed by methods responsible for validation and business logic residing in classes inside the service layer. In terms of implementation using Entity Framework, if I get every individual operation performed by a service layer method to issue a .SaveChanges(), I would lose the ability of having DBContext to behave like a Unit of Work, wrapping up a single .SaveChanges() for many individual DBSet operations. In this case, the DBSets may behave like repositories. Many people argue that Entity Framework's DBContext and DBSet are implementations if Unit of Work and Repository pattern, respectively.
In a more objective question then, how can I implement these patterns using directly DBContext and DBSet, without any further abstraction into new generic or specific entity repository classes and unit of work generic class? The implementation needs to rely on the consumption of a service layer for the reasons I have already stated.
I think an answer to that would be just the last complexity leap I feel necessary to get my "least complex viable design".
Let me put a more concrete example to illustrate:
In my service layer, I have 2 methods to implement validation logic for 2 insert operations, with a programmer defined method Insert such as:
EntityOneService.Insert
EntityTwoService.Insert
Each of these methods in their corresponding service layer classes would have access to a DBContext and use DBSet.Add to signal they should be persisted, in case all validation and/or business logic passes. The desired scenario is that I can use each service layer method call in an isolated way, and/or in groups, such as in a new different service layer class method, such as:
OperationOnePlusTwoService.Insert
This new method would implement calls to EntityOneService.Insert and EntityTwoService.Insert, IN A TRANSACTION-LIKE FASHION.
By transaction-like I mean that all calls must succeed, not violating any validation or business rule, in order to have the persistence layer to commit the operations.
DBContext.SaveChanges() apparently would have to be called only once for this to happen, OUTSIDE of any service layer Insert method implementation. In the context of an ASP.NET Controller consuming service layer methods, how could that be achieved, without actual implementation of a Unit of Work and Repostory abstraction over DBContext and DBSet?
Any advice please would be very much appreciated. I am not posting this to argue the value of a real abstraction and implementation of Repository and Unit of Work patterns, or if Entity Framework's DBContext and DBSet are or are not equivalent to proper Repository and Unit of Work patterns, that's not the point. My project requirements do not involve in any way the need to decouple the application from Entity Framework, or to ideally and fully promote testability. These are not concerns and I am well aware of consequences and future maintainability impacts on not adopting full fledged implementations of half a dozen layers and all design patterns possible to make a big world-class enterprise solution.