Please note that I only have minor experience with the .NET framework and that this answer only relates to the architecture part of your question.
As far as I understood it, you are basically applying the following architectural patterns in your application:
Layers: It seems you have a persistence layer (Repository Pattern), a business logic layer and a view layer.
Model-View-Controller: This pattern is applied in the view layer using ASP.NET MVC.
I guess the question is: what does a BLL look like and what does the MVC controller that uses it look like?
First of all, this architecture is intended for large-scale, enterprise applications as Robert Harvey mentioned in his comment to your question. What is characteristic for such systems is that the domain logic (which you encapsulated in the business logic layer) should be accessible through various interfaces.
Consider Twitter for instance - Twitter could have a business logic layer which provides services for registering and validating users, posting tweets and much more. On top of this business logic layer, multiple other components could exist in the view layer, e.g. a web interface and web service component. Since all business logic is encapsulated in the business logic layer, you can follow the DRY principle and improve maintainability and other quality attributes.
To come back to your question - you should encapsulate business logic and data access in the BLL. The controller (think MVC) should make use of the BLL and should not have direct access to the database. Consider the view layer as an interface to your application.
So I feel like if I have a BLL, I should be passing it the IUnitOfWork, so that it can use that to create the IRepository instances that it needs. But how will the BLL (separate DLL from the front end) 'know' what implementation of IRepository to build?
The BLL should know which repository (data source?) it uses. The layers above shouldn't control this. Again, consider the view layer only as an interface to your business logic.
If you don't need the overhead of a business logic layer, you may also choose to use it solely for data access in form of data access objects.
I hope this helped clarifying the various components' responsibilities.
How large is your application? There's a good chance you're overthinking this. Unless the application is a large, enterprise-grade application, the high degree of loose coupling you are advocating is probably unnecessary.
If you decide that this level of loose coupling is still necessary, create a service layer that returns "service" objects. This fulfills a similar function to View Models in MVC. You will then write code in your service layer that maps objects from your domain model to your service model.
Note that part of your struggle may be due to the fact that, while your Data Repository is returning CRUD objects, your Service Layer should be returning the result of actions. For example:
public InvoiceView GetInvoice(int invoiceID);
returns a InvoiceView
object containing whatever data you wish to expose to the public, including Name, Address, Line Items and so forth, from several different tables/objects in your domain.
public class InvoiceView
{
public int InvoiceID;
public Address ShippingAddress;
public Address BillingAddress;
public List<LineItem> LineItems;
...
}
Similarly, there will be Service Layer methods that simply performs an action and returns an object indicating the result of the action:
public TransactionResult Transfer(
int sourceAccountID, int targetAccountID, Money amount, ValidationToken token);
IMPORTANT: You'll never be able to completely decouple from your data. Your consumer will always have to have some knowledge of the data, even if it's just an object ID or UUID.
Best Answer
These are two different patterns with completely different purpose:
Of course the service layer could group operations that are exposed. For example save a
PurchaseOrder
together with its dependentLineItems
in one operation, instead of an operation for savingPurchaseOrder
and another for savingLineItems
.But this grouping / packaging would not solve the question of which item to update in the database if only one
LineItem
out of 10 in thePurchaseOrder
got changed. It doesn't address either the concurrency issues when several users are trying to update the same PurchaseOrder at the same time. And that's the purpose of the unit of work.The service layer isolates the domain logic from the outside front(e.g. presentation layer). The unit of work is used by the domain logic back-office.