InfoLearner
InfoLearner

Reputation: 15598

C# Architecting 3 Tier solution

I have created a win form solution in C#. The solution has three projects, front layer (FL) business layer (BL) and data layer (DL).

Front layer has all forms. All of the forms implement IForms interface Business layer has all business logic. All of the business logic classes implement IController interface Data layer has all data logic that communicates with the sql database. All of the data logic classes inherit IModel interface

I am trying to use MVC design pattern but I am struggling a bit.
Target: Call a method in a class of BL, when I press a button in a form.

If I add BL dll to FL, then I can call the methods of BL.

BL.Class classObject = new BL.Class(this);
classObject.CallMethod();

In the BL, I want to have the reference of the object that called the method so I tried to add reference of the FL dll but it says Circular dll reference.

I want to have a constructor in the BL class:

public BL.Class(IView view) { MessageBox(view.Name); }

How do I architect my solution to achieve this? Please note that the solution is big so I cannot merge all classes into one dll.

Take care, FM

Upvotes: 1

Views: 2041

Answers (7)

David
David

Reputation: 5456

You can define events in BL for FL to register. Whenever there's a change in the model, BL should fire an event. It's up for the view to handle it.

Your BL is created a message box. What if your view is a WebForm? BL shouldn't know the detail of view.

Also, your BL project shouldn't have a dll reference to FL. You need to declare IView in BL, not FL.

EDIT: This is the structure I used before. Your BL defines the interface. Your view has to implement the interface if it wants to use the BL. BL uses the interface to interact with the view. This way, you can build different views that use the same BL.

BL.dll

public interface IView
{
    /// <summary>Update the view</summary>
    void UpdateView(object myBusinessObject);

    /// <summary>Display message</summary>
    void ShowMessage(string msg);
}

public class BL
{
    // Model and View
    private IView _view;

    /// <summary>Constructor</summary>
    public BL (IView view)
    {
        _view = view;
    }

    public void foo()
    {
        // Do something

        // Show message
        _view.ShowMessage("Hello World");
    }
}

FL.dll

public class FL
{
    private BL _myBL;

    /// <summary>Constructor</summary>
    public FL ()
    {
        _myBL = new BL(this);
    }

    /// <summary>Handles user event</summary>
    public void handleEvent()
    {
        // Call BL to do something

        _myBL.foo();
    }

    public void UpdateView(object myBusinessObject)
    {
        // Update your view
    }

    public void ShowMessage(string msg)
    {
        // Display message to user
    }
}

Upvotes: 2

TalentTuner
TalentTuner

Reputation: 17556

The basic idea behind layered architecture is nth layer should talk to n+1th layer but never cross a layer in between so nth layer cannot jump and directly talk to n+2th layer.

this was a architecture Principle, now coming down to your case.

1- 1st you are talking about the 3 layered architecture , it's not the 3 tier architecture because you are not hosting your DAL OR BL in sepreate server but you are creating individual layers in your solution ( by creating seperate project)

2- As you have three DLLS (FL, BL ,DAL) so now your layering must be FL -- > BL----> DAL where ---> represents talked to.

How you will attain above path (FL -- > BL----> DAL) in your application?

For this , you can add reference of DAL in the BL project and BL reference to your FL project , in this way you can adhere to the architectural Principle stated above.

How to implement MVC in your case?

1- 'C' stands for Controller in the MVC , Controller is a class which is responsible for updating FL and Your Model. So youneed to create a individual unique controller for individual unique functionality in your application ( please note , you don't need to create a controller for each form in your application) so if you have Order Module than create OrderBaseController and pass this controller as a reference to your views (Winform)

2- Controller created in the step 1 can expose model (remember , we have the reference of BL in our FL) which can be consumed by the OrderView.

3- View can change the model through controller( note model is in BL but we have the reference in our FL) but actual database changes still not commited.

How will you do the step 3 above?

    interface IOrderController
{
    bool SaveOrder(order order);
    bool ValidateOrder(order order);
    order GetOrder();
}

public class OrderBaseController : IOrderController
{
    private OrderServiceFacade Orderhelper { get; set; }

    public OrderBaseController()
    {
        Orderhelper = new OrderServiceFacade();
    }

    public bool ValidateOrder(order objOrder)
    {
    }

    #region IOrderController Members

    public bool SaveOrder(order order)
    {
        bool success = false;
        if (ValidateOrder(order))
        {
           success = Orderhelper.SaveOrder(order);

        }

        return success;
    }

    #endregion

    #region IOrderController Members


    public order GetOrder()
    {
        throw new NotImplementedException();
    }

    #endregion
}

we have just implemented a ordercontroller.Now its time to attach this controller to the view.

 public partial class Form1 : Form
{
    IOrderController Controller;

    public order OrderToBeSaved { get; set; }
    public Form1()
    {
        InitializeComponent();
        Controller = new OrderBaseController(); // you have the controller , 
        // controller creation can also be delegated to some other component but that's totally different issue.

        OrderToBeSaved = Controller.GetOrder(); // You got the order object here , once you get the order object you can bind its properties to the different control.



    }
}

you got the fully loaded order object, you can use it's properties to bind the different control on the form.

now it's time to save the order

 private void btnSave_Click(object sender, EventArgs e)
    {
        Controller.SaveOrder(OrderToBeSaved);
    }

here we have deleagted saving logic to the controller , now let's see how to save the from the controller. Befor Controller save the order , it has to go through the chain i.e. it must have some logic to talk to the BL (Controller is in FL)

Since we have the reference of BL in our FL , we can directly talk to BL but we go through one more design concept which we call ServiceFacade , A ServiceFacade a class which will be used to delegate the task from one layer to another layer.

 class OrderServiceFacade
{
    public bool SaveOrder(order order)
    {
       return  OrderDAO.SaveOrder(order);
    }
}

Finally we need to save the order in the database so we need to define a class in our DAL we call it OrderDAO.

static class OrderDAO
{
    static public  bool SaveOrder(order order)
     {
            // put logic to access database and save the data.
     }

    static DataTable GetOrder(int OrderID);
}

it is not complete but it gives you the idea how MVC works in a layered scenarion.

Upvotes: 2

AK.
AK.

Reputation: 763

You won't be able to have both layers know about each other if they're implemented as separate projects (causes circular dependencies).

One way to do it would be to use the observer pattern. Setup events in the business classes, and have the presentation layer subscribe to those events. When the business layer completes its actions, it can trigger the events and pass the results back to the view. The business class don't need to know about the presentation layer, it just knows that there are objects subscribed to it, and avoids creating circular dependencies.

Update: Here's one way to have the BL call the FL using events. It's kind of circular logic, but if you've already structured your code around the 2 layers knowing about each other, then you can consider this.

class BL
{
    public void CallFromFL (int parameter)
    {
        int result = DoSomeWork (parameter);
        if (OnComplete != null)
            OnComplete (result);
    }
    public event Action <int> OnComplete;
}

class FL
{
    void Foo ()
    {
        BL bl = new BL ();
        bl.OnComplete += this.getResult;
        bl.CallFromFL (5);
    }

    void GetResult (int result) {...}
}

Upvotes: 0

Liz
Liz

Reputation: 8958

There are actually suggested designs for this type of thing. Most of the documentation on this is specifically directed at WPF which you will find if you look up MVVM (another name for MVC).

Basically, you will need a fourth dll which is kind of a framework layer. This is what you will need:

a ServiceProvider class - something that implements IServiceProvider.

Why do we need this? Because your FL is going to provide UI services. In your case a MessageBox service. However, you may discover this is so useful, you will want to create other services. Like something to browse for a file or folder. There's more but we'll stick with this for now.

Your ServiceProvider class should also keep a list of the services it knows about, and provide a way to add to this list. A full service provider would probably include the following.

private readonly Dictionary<Type,object> mServices = new Dictionary<Type,object>();
public void Add(Type type,object value)  { ...}
public object GetService(Type serviceType) { ... }
public void Remove(Type type)  { ... }
public T Resolve<T>()  { return (T)this.GetService(typeof(T)); } 

If you make it a singleton class, then you can use this service provide anywhere.

The next job is to create services. In your new framework dll create an interface like IMessageBoxService with a bunch of methods that you might want to call to trigger message to the user. You can start simple with just a function like the following, and add as necessary later.

void ShowError(string message);

You will then need to implement this interface in your FL dll. This implementation will do the actual calls to MessageBox.

The next step is to register your new service implementation with the service provider. So somewhere before the GUI opens call the ServiceProvider.add(typeof(IMessageBoxService), new MyMessageBoxService());

Now in your business logic every time you need to popup a message you can ask the service provider for the message box service, and call the method you want in the IMessageBoxService. Something like this...

IMessageBoxService wMess = ServiceProvider.GetInstance().Resolve<IMessageBoxService>();
wMess.ShowError("My Error Message");

Now you have a clean and beautiful MVC compliant design for popping up error messages without any nasty circular dependencies.

If you want to see more about MVVM and what it is all about you can check out the Microsoft link. Or there is a full MVVM framework that someone has taken the trouble to create for WPF that has a lot of interesting features. If you have time you might want to look at that as well.

Microsoft Introduces MVVM

MVVM Framework

Upvotes: 1

jimjim
jimjim

Reputation: 2503

Why BL needs to know about FL? A real BL is independent of FL, At the moment FL is being implemented using WindowsForms, if later the FL was needed to be changed to WPF then, BL needs to change as well. Then what was layered if change in one layer effects the other layer as well? The idea of BL is that it is independent of presentation layer but can be used by different FL. BL should not make any assumption or need to know about FL.

Upvotes: 0

Jeremy Thompson
Jeremy Thompson

Reputation: 65534

I know your programming winforms but you might like to have a look at the WebFormsMVP or MVVM patterns to get some idea's.

"In the BL, I want to have the reference of the object that called the method so I tried to add reference of the FL dll but it says Circular dll reference."

Specifically in the WebFormsMVP project, arguments are passed between between layers based on the way they are in the .Net framework eg (object sender, mySpecialEventArgs e) - the second argument in the parameter "mySpecialEventArgs" has all the info to pass to and fro.

Upvotes: 2

David Neale
David Neale

Reputation: 17018

You are trying to inject presentation logic into your business layer. Your BL should not be creating message boxes - that is the reponsibility of the presentation layer.

Upvotes: 4

Related Questions