If you were to build a badge system similiar to how SO does it, would you put the logic/business layer in the database directly (via stored procedure, scheduled sql jobs) or put it in the server side?
From what I can think of, you have to:
- list badges that pertain to the current user action
- check if the user has a badge already or not
- insert badge for user
Potential options
- business logic in the web application that calls stored procedures etc.
- stored procedures ONLY
- sql server job that runs every x minutes
- windows service that runs every x minutes
Would a combination of these be required? I think it would since some badges are based on milestones for a given question, maybe a batch job is better?
Update
A system where you can modify the badge system, then re-run the entire badge linking for everyone would be even better. i.e. say you change the logic for some badges, now you have to re-apply it to all the questions/answers/votes/etc.
interesting problem to solve!!
I would recommend putting all business logic in the business layer. I recommend this for a few reasons:
- Keep the business logic in one
language / place
- Scalability -
You can partition data, implement
different caching mechanisms, etc.
- Seperation of concerns - let your DB do what it does best...store data, let your programming language make decisions on that data.
I would put it in the business layer, after all this is business logic that we are talking about. Stored procedures can of course be used to pull back the appropriate data, but I am not a fan of implementing decision logic solely in the database. If nothing else just because it becomes harder and harder to track what is going on when revisiting the code later on.
Personally, I'd leave the database to do the data storage / retrieval and have the logic in a 'business layer'.
Following the success of StackOverflow, I'm quite interested in implementing an achievements system for one of my sites, too - so I've been giving this some thought myself.
I'm currently trying to assess the value of having a lightweight (in terms of processing power) routine which I could run in response to specific user actions (up-votes, new answers, etc.) which could keep most of the badges up-to-date as the site goes.
This would be supported by a more heavyweight routine which could recalculate every badge from scratch. This could be run periodically from a service (or at least a simulated service)
to make sure nothing had been missed - but also in response to a change in badge rules.
I guess a big part of the answer to this is going to hinge around the data you're basing the badge awards on. The StackOverflow badges appear to be based on both data (answers, questions, votes, etc.) and events (edits, re-tagging, etc.). So - the badge algorithm must presumably be interrogating some sort of audit log or 'actions' log.
So - this is a classic SO debate and a debate amongst passionate programmers. I've asked a similar but more generic question about it...
business logic in database layer
To answer the first part, I found one of the best explanations I have seen about business logic in code vs database is here:
http://www.codeproject.com/KB/architecture/DudeWheresMyBusinessLogic.aspx
It goes on to explain why business logic is much better and scaleable. I'm on that mindset as well... so to answer your question, I would keep no business logic in the DB or stored procs, for the main reason amongst many others being that SPs aren't version controlled, and its a pain to version control them. Not to mention, IDEs for SPs are infinitely more primitive than IDEs for code. And sql/tsql and things like that were not meant for complex code structure, but basic manipulation of data, and as you'll see in the article, for some very basic code stucture because previously client-server architecture was limited.
Some excepts from this page:
In a client server system there are two tiers thus forcing at least two layers to be implemented. In the early days the server was simply viewed as a remote database and the division was seen as application (client) and storage (server). Typically all the business logic remained in the client, intermixed with other layers such as the user interface.
It did not take very long to realize that the network bandwidth could be reduced and logic centralized to reduce constant redeployment needs of the client by moving much of the business logic out of the client. As there were only two layers, the only place to move the business logic to was the server. The server architecturally was a well suited place in a client server system, but the database as a platform was a poor choice. Databases were designed for storage and retrieval and their extensibility was designed around such needs. Database stored procedure languages were designed for basic data transformation to supplement what could not be done in SQL. Stored procedure languages were designed to execute very quickly and not for complex business logic.
But it was the better of the two choices so business logic was moved into stored procedures. In fact I would argue that business logic was shoe horned (smashed in, made to fit) into stored procedures as a matter of pragmatism. In a two tier world, it was not perfect but was an improvement.
The business layer should contain all of the business rules.
Such a design has the following advantages:
- All the business logic exists in a single location and can be easily verified, debugged, and modified.
- A true development language can be used to implement business rules. Such a language is both more flexible and more suited to such business rules rather than the SQL and stored procedures.
- The database becomes a storage layer and can focus on efficiently retrieving and storing data without any constraints related to business logic implementations or the presentation layer.
So - now, in regards to the architecture, I would do it so that each users badges would get updated via calling a stored proc when the related question/answer or anything else gets updated. You put this in the business logic of the question or answer, as I assume that it will be different for different types of items (when they get modified). Because this is a event based action, the action would only happen when the event happens. If you have a service or scheduled tasks, it will run all the time, and though minimal, it will bog down the system eventually when you have a lot of users.
If you don't want to have each users' events to trigger a gazilion checks and updates, you can batch them into a table, and have a scheduled job to update the badges.
To allow for the system to update an entire userbase based on new business logic, you can encompass all your actions into a windows job, or a one time job, and that would function better than tsql, IMHO, and would be much more maintable, and flexible.
However, sometimes it may benefit you to duplicate VERY little of the business logic, for some performance gain. But as you see in the article, business layer in code is much more scaleable, so it may be a moot point.
Hopefully this is useful information for you, to decide on what you need...
I would write a stored procedure, since all the information needed resides in the database so this is the most efficient place to access that data.
A tyical rule could be implemented via a single INSERT statement along these lines:
IF eligible for <new badge> THEN
INSERT INTO user_badges
SELECT <new_badge>
WHERE NOT EXISTS (SELECT NULL FROM user_badges
WHERE badge = <new_badge>);
END IF;
(I simplify somewhat!)
I would strongly recommend leaving decisions to business logic, not to stored procedures. Stored procedures are for processing data (i.e. gathering data, checking for particular states and conditions, deleting, updating, aggregating, etc.). It's not a create place for conditional logic (decision making).
As for events, verses data: everything in a merit system is (or at least can be) event based.
data (answers, questions, votes, etc.) and events (edits, re-tagging,
etc.)
...All of these are events: answering a question, asking a question, casting a vote, etc.
You can use stored procedures to get the data you need to determine if a badge has been earned, but your code should actually make the decision, and if appropriate, assign the badge. The algorithms for this would be as varied as the badges, perhaps. However, here is a little logic I would follow:
- Categorize all badges based on the types of events they involve (e.g.
answering a question, asking a question, making an edit, retagging, voting, etc.)
- When a particular event occurs, grab all the badges associated with
that event (i.e. that can be earned by completing the task that
triggered the event)
- Cycle through each badge in that category and run it's Badge.VerifyCriteria(UserID)
method
- If user doesn't already have the badge, make the badge assignment
The VerifyCriteria method would be a good example of a possible place for a stored procedure, especially if you need higher performance. This is as much checking the database for a particular state or condition, as it is business logic. Some badges may require some processing that is difficult in a database language (e.g. SQL), so VerifyCriteria should be an actual method on the object which calls stored procedures and/or code as appropriate. This also keeps your business logic OO-friendly.
If you want to completely re-badge everyone in the system, I would either run batch jobs in the background, or if the processing is light enough, just update the user the next time they log in, or the next time they're user data is accessed (e.g., when someone tries to view their profile). But I would keep it all in the business logic still.
I would concentrate all business logic in a single language logically separated in namespaces or packages. All the work needed to be done by web interface for instance, would be exposed by the server using services.