An architectural pattern is a general, reusable solution to a commonly occurring problem in software architecture within a given context. Architectural patterns are similar to software design pattern but have a broader scope. - https://en.wikipedia.org/wiki/Software_architecture
MVC is a commonly used pattern. It is used in Rails, AngularJS, BackboneJS, .Net MVC, Laravel PHP, and Java Spring MVC.
Separation of concerns: the established way for architects to reduce complexity is to separate the concerns that drive the design. - https://en.wikipedia.org/wiki/Software_architecture
We want to give different pieces of code different responsibilities. Basically we don’t want one piece of code trying to handle everything itself so we break up similar parts or functionality into different files.
MVC stands for model, view and controller. We place all of our code having to do with data storage inside of a model. All of the code that will be presented to the user (in Rails this will basically be the web page itself) is placed inside of a view. And the business logic or glue that connects these two pieces together goes into the controller!
The model encapsulates the data. The view is the presentation layer it's what the user sees and interacts with. The controller is going to process and respond to events such as user actions and it's going to invoke changes to the model in the view based on that it's going to make decisions for us and control what happens.
We will use a bar example (similar to the party parrot) to explore this pattern. The scenario is that we want to order a drink from the bar.
What are the different parts of ordering a drink at a bar?
Let’s leave out the money side of things for now and focus on the 3 most important parts.
- The information the customer is presented with
- The bartender
- The bar (holds all of our drinks)
The first step is to understand exactly what a model does in our MVC pattern. A model is used for interacting with all of our data. Any functionality such as creating, reading, updating or deleting is within the model.
So out of the 3 above pieces in our scenario which piece do you believe should go in a model?
The bar! Lets create a bar class that will hold all of our drinks.
class BarModel
def initialize
@drink_amounts = {
beer: 0,
martini: 0,
coke: 0
}
end
end
Done? Nope! We haven’t added in any functionality yet.
What functionality might we need for our Bar model?
Getting a drink Knowing whats available Restocking Ok so now we know what our Model needs to do lets code it in.
class BarModel
def initialize
@drink_amounts = {
beer: 0,
martini: 0,
coke: 0
}
restock(beer: 20, martini: 20, coke: 20)
end
def restock(beer: 0, martini: 0, coke: 0)
@drink_amounts[:beer] += beer
@drink_amounts[:martini] += martini
@drink_amounts[:coke] += coke
end
def get_drink(key)
if @drink_amounts[key] and @drink_amounts[key] - 1 >= 0
return @drink_amounts[key] -= 1
end
end
def get_available_drinks()
drinks = []
@drink_amounts.each do | key, value|
if value > 0
drinks.push(key)
end
end
return drinks
end
end
Now that we have our model which deals with all of our data (in this case our data is drinks). Lets move on to the view.
A view is all of our interaction with the user. In a web application a view is literally what the user can see. It is our rendered web page. In our example the view is going to be the different prompts and interactions with our customer trying to order a drink.
What are the different interactions we can have while ordering a drink at a bar?
- Greeting
- Reading menu
- Ordering a drink
- Being asked if I would like more
- Goodbye message
- Lets code all this into our view
class BarView
def greeting
puts "Hi there how can I help?"
end
def selection
puts "Enter (1) to look at a menu or (2) to order a drink"
gets.chomp.to_i
end
def read_menu(items)
items.each do |item|
puts item.capitalize
end
end
def order
puts "What would you like?"
gets.chomp.downcase.to_sym
end
def wrong_answer
puts "Sorry I didn't quite understand you"
end
def goodbye(item)
puts "Here is your #{item}. Thaks for coming, see ya later!"
end
end
So now we have to connect these two pieces together.
Our controller is the glue that connects our models and our views. This is where all of our logic goes to decide what to do next. In our scenario that would be the bartender. They are the one that will interact with the customer and also our bar.
class BarController
def initialize
@model = BarModel.new
@view = BarView.new
end
def run
selection = greet_customer_and_take_selection
items = @model.get_available_drinks
if selection == 1
@view.read_menu(items)
end
order = take_order(items)
@view.goodbye(order)
end
def greet_customer_and_take_selection
@view.greeting
while true
answer = @view.selection
if (answer != 1 and answer != 2)
@view.wrong_answer
else
return answer
end
end
end
def take_order(items)
while true
order = @view.order
drink_count = @model.get_drink(order)
if drink_count != nil
break
end
@view.wrong_answer
@view.read_menu(items)
end
order
end
end
Lets run our app and see it in action!
controller = BarController.new
controller.run
Now we could easily extend out our application to have more logic and more advanced interactions without our code becoming a jumbled mess. That’s what is so great about the MVC pattern. It allows us to build some pretty complex applications that don’t become cumbersome to maintain because all of the code has been abstracted into logic components.
Challenge details in `bar_runner.rb` below
Ruby on Rails: Understanding MVC architecture - short video
What is MVC - freecodecamp
10 Common Software Architectural Patterns in a nutshell