-1

I'm working on a vending machine project, and i tried to split it into UI and BL layers, But I'm getting into problem. For example I have this function for paying in coins, which derives from an abstract class:

public override void Pay(decimal amount)
{
    while (currentCoins < amount)
    {
        // Print instructions
        // Get input

        if (Valid)
        {
            // logic
        }
        else
        {
            // Print error
        }
    }
}   

So the problem is that i dont have access to the UI inside the BL, but i need continuous communication with the UI. I thought of making function for each payment method in the UI, but it violates OCP...

I'm looking for an elegant solution ideas that won't break SOLID principles.

Is there any desgin pattern or something that can solve my problem? (strategy pattern?) I would appreciate any guidance / idea.

Thanks :)

2 Answers2

0

You can use the Observer pattern

using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Text;
using System.Collections.Concurrent;

public class program
{
    public static void Main()
    {
        var robot = new Robot();

        robot.Died += (sender, eventArgs) => Console.WriteLine("Robot Died");

        robot.TakeDamage();
        robot.TakeDamage();
    }
}

// --- Everything above this line would be in the UI layer
// --- Everything below this line would be in the business/domain layer     

public class Robot
{
    private int _health = 2;
    public EventHandler Died;

    public Robot(){}

    public void TakeDamage()
    {
        _health--;

        if (_health == 0)
            Died.Invoke(this, new EventArgs());
    }
}
  • Thank you! I did exactly what you said, but yesterday i read online that the BL shouldn't reference the UI at all, so i changed all my design.. oh gosh. Is it ok that the BL references the UI? and btw i need also print some menus in the UI, wouldn't it cause circular reference? –  Apr 21 '20 at 20:32
  • Oh nonono... you are making mistakes in your architecture xD the domain layer references nothing... it should be the core of your system. The UI layer references the domain layer, not the other way around. – Davey van Tilburg Apr 21 '20 at 20:34
  • @Ba2sik are you saying you render new UI elements from the business layer? Oh gosh xD well... that should also be handled then with dependency injection. The business layer only has the logic that says: "Ok... now we need a button". But the UI layer, through DIP, will receive the call and actually renders the button – Davey van Tilburg Apr 21 '20 at 20:37
  • @Ba2sik In my proposal, the UI layer references the business layer. So... ehm, maybe you misinterpreted me? – Davey van Tilburg Apr 21 '20 at 20:37
  • ummm I meant in visual studio to do Add Reference to the UI dll (How would the BL know the about the UI if I wouldn't do this?) –  Apr 21 '20 at 20:40
  • @Ba2sik No no... DIP... dependency injection. Gosh this is hard to explain. Okay, the UI layer references the BL layer. The UI layer creates the concrete implementation of IDisplay called ConsoleDisplay. The UI layer instantiates the Robot and with creation passes a reference from ConsoleDisplay to Robot. Now the domain object... Robot, has a reference to an object of which the type lives in the UI layer. This is because UI has access to IDisplay from the BL layer. And ofcourse Robot wants to have an instance of IDisplay. Does that make it any clearer? – Davey van Tilburg Apr 21 '20 at 20:50
  • @Ba2sik As in my example, you see that code above the comment line uses code below the comment line. But not the other way around. – Davey van Tilburg Apr 21 '20 at 20:51
  • Yea, I get it that we're injecting the display type to the BL, but I'm talking technically, I have one project for BL and one for UI. So for Visual Studio to recognize the IDisplay type in BL, it asks me to add the UI project reference to the BL project... otherwise it wouldn't recognize the type IDisplay and will cause compilation error –  Apr 21 '20 at 20:56
  • @Ba2sik IDisplay should be in the BL layer... please look closely at my example – Davey van Tilburg Apr 21 '20 at 20:57
  • Ohhh you right! I missed it. Thanks!. Is it common to make interface in one project and implement it in another? and btw, in another question I asked - https://stackoverflow.com/questions/61341763/3-layers-architecture-how-should-i-seperate-ui-from-bl?noredirect=1#comment108516626_61341763 , someone commented that the BL shouldn't be responsible *at all* on getting input from user and printing it (and here we are printing it). so it's fine? –  Apr 21 '20 at 21:08
  • @Ba2sik The opinion on that is very spread out... I think usually less experienced people with architecture will say that the BL should not interact with the UI. But what if your business is having a domain that is a generic framework for certain type of UI's? See... the business layer is subjective... It should encapsulate the business of your system without having any dependencies to peripherals. So that the BL is only that... your business for your company. All the details such as database interaction details and ofcourse also UI details. Should not be in it – Davey van Tilburg Apr 21 '20 at 21:13
  • @Ba2sik But ofcourse... if the business of your system is not being an framework for certain type of UI's.. then no, the business layer should not contain anything regarding UI interaction. For games for example, the business layer would consist out of player interaction, maintaining position on the map and making already translated input have actual effect on the player objects. But the interface layer reads the player objects and actually renders them. The business layer does not ask the UI to render the players... its a hard concept. – Davey van Tilburg Apr 21 '20 at 21:17
  • @Ba2sik Also not to be... that guy, but i'm doing my best to both help you, and get some rep. Trying to get to 2K, would you mind helping me as well by giving me a vote or choosing my answer? – Davey van Tilburg Apr 21 '20 at 21:19
  • @Ba2sik But fair enough, this conversation has made me realize that i made the error in assuming that abstract UI interaction is a part of your domain, so i've changed the example to be more appropriate – Davey van Tilburg Apr 21 '20 at 21:24
  • Wait.. so events are the way? I'm a little confused. I wanted more to use polymorphism and DI. And don't worry I'll choose your answer once I get it :) –  Apr 21 '20 at 21:33
  • @Ba2sik Both DIP and the observer pattern are ways to invert flow of control. It depends on the project which is more applicable. maybe tell me about what your project is so i can give a more informed opinion. now im just geussing to be fair – Davey van Tilburg Apr 22 '20 at 05:30
  • As I said, this is vending machine, so it includes many menus and interactions from the user (inserting coins/credit card info, choosing products) –  Apr 22 '20 at 06:40
  • @Ba2sik Then I'd say that UI interaction is not a part of the business layer and the observer pattern (use of events) is the way to go. Reasoning for this is because all operations of a vending machine can be conceptualized without having the need for UI interaction. The entire business layer can be created and tested through unit tests that mock commands (command pattern) and check if events were raised or certain output is returned. Business layer for you (i think) should not include abstractions of UI. But that is my humble opinion – Davey van Tilburg Apr 22 '20 at 06:47
0

You must create at least 3 layers for a uniform layered architecture. It is important to divide business and data communication. For example, if it is necessary to create a structure;

  • Entities
  • Data Logic Layer
  • Business Logic layer
  • Presentation Layer

So your sturcture should be like that;

  • /Connections /entities /DLL
  • /iDLL (interfaces/abstract classes)
  • /BLL
  • /iBLL (interfaces/abstract classes)
  • /UI

You should define your functions in the iDLL for data communication. For example save or calculate or payment etc. Then implement to DLL these functions. After then BLL class should be extends DLL class. In the BLL you create your program logic.For Example; check until the current money is equal to the fee, if it is equal, send it to the data layer. While doing this, you can hold the coin in the sturctur and call it from the presentation layer, or you can set up a reverse logic, check it in the presentation layer, and then validate it in the business layer. I think second logic the best. Almost web project runs like that.