I often question myself of where to put the logic that organize the data to a view that is not necessarily a business logic in a 3-layered architecture (DAO, Business, View).

I'll put an example to be clearer:

I have a UI in my aplication that shows pending tickets and answered tickets in two different tabs. To get the data from the database, I make a single query. Since there is no business logic, I'm returning the whole list to the Controller (View) and separating it in two lists to pass to the view template.

Should I be separating this in the business layer? Or is it correct to separate in the controller, since the logic is attached to that single UI?

  • 2
    Do your business rules change if you later decide to show it as one list? If not, then the separation is a pure UI concern and should happen there. – Bart van Ingen Schenau Jul 17 '14 at 6:36
up vote 3 down vote accepted

I like where you are going with this curiosity, so I'm going to bring up something else I see.

To follow not only "Separation of Concerns", but also "Persistence Ignorance", it is almost always a Very Bad Idea to allow your UI layer to call into your business/data side with a "Get All" (Or even close) method. I realize there may be a constraint or two like Where UserId == LoggedInUser, but that is still too close.

The reason being, suddenly you have clever UI Developers using the "Get All" method, and performing business/Data-Access logic on the UI to separate out information, pick and filter through, etc.

Even if you tell yourself that will never happen, or that you're the only one in the next 100 years that will touch this code and you Promise yourself you'll never do that, you still shouldn't open the UI up to such power.

Now imagine some time in the future a single UI Control needs to display JUST the Answered (or pending) tickets. What do you do now? You have to "Get All" and perform your filtering logic on the UI side?

I wouldn't even put that into the Business Logic. This is about data separation. You should have as finite queries as possible in your Data-Access layer, and expose only those required results to your Business Logic. Your Business Logic in this case may just be a wrapper to return a Data-Access result, but that's okay. That's the BLL's responsibility to determine the Persistence object and route calls.

Now, if you need a list on your UI side that shows "All" Tickets for a user, it may be prudent to have that "Get All" method where the only filter is the UserId. However, it may be more prudent to combine the Pending and Answered Queries. Why?

What if in the future you add a "Deleted", "Removed", "In-Flux", "New" or any other kind of status? Now you have to re-write code because all you wanted to do is show a Combined List of Pending and Answered questions.

So give your UI less control over massive lists, and instead allow it to pull back only the limited information it needs, and alter/combine from there.

If I have a model definition called Tickets, then I would put class level function on that class to give me a collection of Ticket instances.

example:

class Ticket
    # these are class level function, you call them like this
    # array_of_pending_tickets = Ticket.pending_tickets(2)
    def self.pending_tickets(category_id)
        # do the query to get pending tickets here
        # and return an array of new Ticket instances
    end
    def self.answered_tickets(category_id)
        # do the query to get only answered tickets here
        # and return an array of new Ticket instances
    end
    # this is an instance level function, you call it like this
    # ticket_im_working_on = array_of_pending_tickets[0]
    # ticket_im_working_on.mark_as_answered('hello world')
    def mark_as_answered('comment')
        # the instance of the Ticket class should have some
        # kind of internal instance variable like @id that 
        # you can use to modify the database
    end
end

Once your model is defined like this, then you use your controller to execute the proper model function (say, if you have /pending/category_1 as your url, parse that in the controller and then get the variable), and then once you have the data you need use the view to display it (using a loop on the array from Ticket.pending_tickets(1)).

Consider T-SQL. You can query a database using whatever where clauses make sense.

I see no reason that your database layer couldn't query based on criteria. I would not construe simple filtering as mixing business logic and database logic. If you gain a separation of concerns only to throw performance out the window, there's no win.

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.