0

I've been recently trying to go slightly further in arch. patterns (specifically layered arch.). I decided to go though an old project of mine (a CLI-based cinema reservation system) and evaluate my design from back then.

This an ambitious attempt to implement the ECB pattern (Entity-Control-Boundary Pattern), i didn't go all the way with it though (as you can see in the Movie and Reservation classes). I understand the differences between ECB and MVC, generally revolving around what to be encapsulated in the model/entity, plus the rules of directionality between the layers.

This is essentially the class diagram I started out with:

enter image description here Figure 1

I refactored this to ECB (from my understanding) as follows: Note: It should have been LoginControl and CreateAccountControl not _Controller

enter image description here Figure 2

My final DCD is as follows (note: I didn't apply ECB on the Movies and Reservation classes):

enter image description here Figure 3

I have 4 main questions regarding this design: (would appreciate to hear the best practices from you)

1. Is this an acceptable design of the ECB pattern?

Notes:

  • A new user can either create account or login
  • A logged in user can: logout, make reservation, view reservations, view movies
  • I decided to separate the user interface logic for when: 1.a user is new, 2. a user is logged in, so I created the two boundary classes.
  • I made the entity classes (talking about Account only here; I didn't refactor Movie and Reservation into ECB) to contain only attributes and getters/setters.
  • I placed the business logic (again referring to the Account entity) into a separate Control class based on each use case (login, registration).

2. Are these boundaries reusable for a future integration?

Notes:

  • pick_seats, pick_movie, pick_time, login_inputs, create_account_inputs are all CLI methods that take inputs from end-user and pass it (based on the chosen command/service from the end-user as well) to the relevant method of the relevant controller.

  • Say that I would like to add a web framework like flask to this system, I believe that the boundary classes, with a better design (e.g.: having an interface or a base class) can be reused, and the input methods would be implemented differently to instead, fetch input field values from forms via POST requests. Please correct me if I'm wrong.

3. Is it right for my driver code to instantiate all the needed objects, especially Entity objects, and perform the associations accordingly

Here's a snippet of my bootstrap.py, which wires everything together:

bootstrap.py

# ===================== SYSTEM IMPORTS ========================
from Account import Account
from CreateAccountController import CreateAccountController
from LoggedInAccountController import LoggedInAccountController
from NewAccountUI import NewAccountUI
from LoggedinAccountUI import LoggedinAccountUI
from Movie import Movie
import time

# =================== Object Initialization ===================
acc = Account() # entity
ca_cont = CreateAccountController(acc)
na_ui = NewAccountUI(ca_cont)


mov = Movie() 
lg_cont = LoggedInAccountController(acc, mov)
lg_ui = LoggedinAccountUI(lg_cont)

...

Notes:

  • I chose to implement the relationships between my classes as associations, to lower the coupling (+ imports) in the system internal classes, but the trade-off was that the coupling in the driver code became really high hence the imports. But what would be the alternative?

4. Would this be considered an acceptable DAO design and implementation ?

  • I included the same ECB-based system diagram from Figure 3, but with the focus only on the Entities in the system, as shown in Figure 4 (see attributes highlighted in red)

enter image description here

Figure 4

  • Since I was working with JSON files at the time, I resorted to a single concrete DAO, I named it StoredFiles, the constructor takes a JSON filepath, reads its content and store it in an instance variable called memory.

StoredFiles

# StoredFiles
import json
import os
cwd = os.path.dirname(os.path.realpath(__file__))

class StoredFiles:

    def __init__(self, filename):
        self.stored_file = cwd+"\\"+filename
        self.memory = self.read()

    def read(self):
        afile = open(self.stored_file, 'r')
        return json.load(afile)

    def get_all(self):
        return self.memory

    def update(self, **kwargs):
        ....

    def write(self):
        ....

    def search(self, key, val):
     ...
  • The idea was to create a DAO or a StoredFiles object for every entity.
  • In each entity, a StoredFiles object is instantiated by passing the relevant JSON file as an argument to the constructor.
  • I chose to link Entities to their DAO objects via the <<create>> usage dependency relationship, and to keep them as class (or shared) attributes.

For example:

Account.py

from StoredFiles import StoredFiles

class Account:
    accounts = StoredFiles("data.json")

    def __init__(self):
        ...

Movie.py

from StoredFiles import StoredFiles

class Movie:
    movies = StoredFiles("movies.json")

    def __init__(self):
        ...

And so on..

  • I could have them linked via Composition (as instance variables) but from a conceptual/logical perspective, composition didn't feel right to me.

So essentially the control flow is as follows:

Boundary -> Control -> Entity -> StoredFiles/DAO

IMPORTANT NOTE I've looked up ALOT of resources online, what I came to realize is (please correct me if im in the wrong or I have missing information) that DAOs essentially need to have an interface (or an abstract class) and should be implemented (or sub-classed) for every entity in the system. And now by looking at current flow described in this system I'm presenting, it seems slightly incorrect towards the end, and the correct one should be:

Boundary -> Control -> StoredFiles/DAO -> Entity

But from an analytical POV, taking this design/flow as it is, what is the good and the bad about it? If I stayed with the same design how hard would it be to workaround a new persistence, e.g.: an SQL engine for example? What is a best practice that I can follow to recreate this DAO.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.