An open source StackOverflow clone by a n00b web dev
Edit – just so you know, since writing this I found Shapado and OSQA, which seem well underway to becoming viable Stack Exchange alternative. I don’t think I will continue developing this project, although it has taught me a great deal nonetheless.
I wanted to share a small learning exercise I underwent recently. I decided to learn how to build a website, and share the experience here. Lacking an original idea at the moment, I decided to create yet another StackOverflow clone – not original, but a good exercise nonetheless. The code for the project is hosted at GitHub, although I don’t have a live showcase up at the moment. Yes, I am aware a google search reveals an existing open source SO clone called Stacked – I thought building one from scratch might teach me more than reading someone else’s code base (I could be wrong, but this is how I wanted to roll).
Day 1
The choice of database engine was easy: The one database I had any experience with was mysql, and being open source, free and easy to work with, I went with it. Next, an ORM library. After some digging, I’ve decided to go with NHibernate + Castle ActiveRecord. ActiveRecord is great for easily mapping simple classes and relations, and NHibernate to complement it in ‘advanced queries’. I don’t get any LINQ magic from these ORMs, which is a pity. I then proceeded to create a solution and some projects, added NUnit as a test framework, and setup Castle MicroKernel as an IOC framework.
I tried to found an open hosted source CI server, but didn’t find anything that worked. Oh well, not essential for now.
Day 2
Before proceeding any further, I had to choose a source control provider. If this were a production project I would probably have gone with SVN hosted on Google Code. However, Ken told me about Git long ago, and I thought this was a good chance to experience it. So, on to GitHub. Opening the project was a no brainer, but finding a decent client was more challenging. I had some fun learning about Git’s private keys, and configuring TortoiseGit (I tried GitExtensions, but it doesn’t support Visual Studio 2010 beta 2 yet). Overall, TortoiseGit gets the job done, after some tweaking and .gitignore files.
Git’s distributed source control model is interesting and worth a try.
I created the User, Question & Vote tables, learned about composite keys in AR, and wrote my first NH query:
GetVoteCount – “SELECT Vote, COUNT(*) FROM ” + VotesTableName + ” WHERE PostId = :postId GROUP BY vote”
I currently don’t have any caching on the vote count – am simply storing the votes as relations between users and questions, and counting them on the fly.
Day 3
I would like to implement full features, not write the entire DAL first and then the application logic. So, it’s time to start learning about web development. At work people are using Monorail, but after reading this question I decided to try out ASP.Net MVC instead. So, I read a basic tutorial and starting coding. Some things I learned:
- I finally got the meaning of Global.asax.cs – it’s simply the ‘main’ of the web application.
- By default, ASP.Net MVC creates the controllers by itself and does not support IOC. Fixed (remember to setup the Controller’s lifestyle as LifeStyle.PerWebRequest).
- Some Asp.Net MVC basics:
- Use <%= … %> to write to the output stream (that gets sent as the HTML), and <% %> to simply execute code.
- Use Html.RenderAction() to create links to other pages (~= Actions)
- Your pages are butt ugly without tweaking the CSS
Day 4
- I quickly caught myself duplicating code, and turned to learn about Partial Views, which are reusable View pieces.
- I realized that having my model entities derive from ActiveRecordBase is damn ugly, because it makes my entire application dependant on AR even if I was using repositories to access the data. I switched the repositories to using ActiveRecordMediator instead.
- The magic that is ReturnUrl – an extra request parameter that controllers use to return you to your original page after you login.
- An interesting usage for anonymous object creation syntax in C#, to pass query parameters: new { ReturnUrl = “foo”}
- I decide to create a base class for all my controllers – UserAwareController. This was needed because every controller needs to access the current user, so I put all this logic in the UserAwareController base class.
- Since every View needs the User to render, I created a Model base class that contains the current user. I’m not sure if this is the best way to go here, but it worked (what are your recommended best practices to store the user data?)
- I implemented OpenID login using DotNetOpenAuth, and it was quite a breeze. No need to store user credentials, just store his public open id and let other websites handle the authentication for you.
Day 5
After allowing users to login and post questions, the next thing I wanted to implement was voting. So far all the code was server-side, but voting requires javascript because when a user votes you don’t want to refresh the page, but rather just change the vote icon. So:
- I learned about jquery basics and wrote events to handle clicking the voting buttons.
- Sent the vote information to a dedicated controller using JSON. The way JSON requests translate to controller methods is really seamless.
- Initially I had an ‘AddVote’ method, but quickly switched to ‘UpdateVote’, which makes more sense.
- Some css tweaks to make the cursor change to a pointer while on the voting buttons
Day 6
- I finally had to cache vote count on questions & answers. The total vote count / score of a question has to be indexed, because we’ll have pages that get the ‘hottest posts’, and so keeping the User-Question vote relation is not enough.
- So far, all my entities were strictly mapped to database rows. Now, I had to create a new ‘rich entity’ that contained a post and the current users’ vote on this post.
- Finding myself duplicating logic between questions & answers, I create a Post base class and factored the entities and repositories to work on abstract posts.
This is it for now. I hope I didn’t make too many glaring mistakes in the process. As I mentioned, the code is available at GitHub – if you’re interested in helping develop it or have any questions, please let me know.
Tomer Gabel:
First off, wicked cool – I never did get off my ass and learn web development, and it’s a definite hindrance.
14/2/10, 9:59Re UserAwareController, isn’t it easier to implement the user authentication logic as a filter which pre-handles authentication and places the user token in the request context?
Eli:
Nice post, Ron, well done.
It’s a shame you didn’t use the opportunity to practice a decent language & web-app stack like Python/Django, instead of keeping with the Borg 🙂
14/2/10, 20:13ripper234:
@Tomer – I wasn’t aware of the existence of filters. I’ll have to try it out, it might be indeed an easier solution.
@Eli – I am quite comfortable with most of the .Net ecosystem, and part of the purpose of this exercise was to deepen my understanding of some parts of it that were in the dark until now. Learning other languages is good, but deeper knowledge of one’s “chosen tool of the trade” is essential.
15/2/10, 10:32Ken Egozi:
@Ron: super-cool.
as for “code review”, looking at the UserAwareController, I’d have made
protected User CurrentUser
a method (GetCurrentUser()
) as this is not a Property of the controller, and also to denote that we might go out of process to get it@Tomer: Filters in MS MVC is also new to me. Good to see that they progress nicely
@Eli: Calling something else from what you like “the Borg” is … borg-ish. I believe in options, and the new MVC stack (along with the older MonoRail and newer FubuMVC) are very good options if you want to kee in C# land, as Py/DJango is great if you like the Python ecosystem
15/2/10, 14:25options and choices, that’s the thing.
ripper234:
@Ken – R# –> Property To Method, done – thanks for the comment.
15/2/10, 22:14Shani Raba:
wow, amazing.
20/2/10, 10:33Tuan:
Hello,
10/3/10, 16:29Nice article, I have tried to do exactly the same thing few months ago with a goal to build some kind of open source stack over flow, which support multiple languages ( since I want to build one for developers in my country). I got to the point of implementing comments, points and badges ( used mysql and hibernate for my data access as well), then the project start to gather dust until now. I always looks for some one to work with on the project, and will be happy to join in if you want more development on this.
THanks
Bruce:
Hello,
19/5/10, 17:31Nice article, I have tried to do exactly the same thing few months ago with a goal to build some kind of open source stack over flow, which support multiple languages ( since I want to build one for developers in my country). I got to the point of implementing comments, points and badges ( used mysql and hibernate for my data access as well), then the project start to gather dust until now. I always looks for some one to work with on the project, and will be happy to join in if you want more development on this.
THanks
ripper234:
@Bruce – take a look at OSQA and Shapado, both of which are under active development. I strongly think that anyone that wishes to build a Q&A platform should join forces with one of them.
19/5/10, 22:00vietgeek:
http://vietgeek.somee.com, stackoverflow clone with asp.net mvc3
21/1/11, 17:29