FarmMaster

During my time as a student at AIM Educational Ltd, one of the projects I began work on was FarmMaster: a website solution for managing and tracking livestock, and various other bits-and-bobs related to farming.

This project page isn't as colourful as my other project pages are, due to the fact that this is an abandoned project. However, I still want to document this project, as all of my current knowledge about web development and system administation is all thanks to FarmMaster.

There were 4 versions of FarmMaster in total. Version 1 seems to be lost to time. Version 2 requires code that probably doesn't exist anymore. I was however able to get versions 3 and 4 running, so their sections will come with a video clip, showing off most of the functionality I got around to implementing.

Version 1

Version 1 was more of a learning project than a serious attempt at FarmMaster.

I used this version to introduce myself to horrible yet wonderful world of web development. I learned about: Bootstrap; HTML; ASP Core fundamentals; EF Core fundamentals (database-first), and some very basic Javascript.

I don't quite remember how much we implemented within this version, but this is where I layed the groundwork of my knowledge for future attempts.

Version 2

In this version I was still using bootstrap, however now that I was more comfortable with web development and usage of ASP Core, I felt more confident in being able to better design the codebase to cover my current and future needs.

This version was using basic AJAX GET and POST endpoints for anything dynamic, but for the most part the website was still mostly form-based.

This version had some interesting things though - I started to dip my toes into TypeScript, and the awful yet convenient world that is NPM.

I had also created two libraries which I thought would've helped me keep the code clean and sane: AimDataMapper, and AimLogin.

AimDataMapper was a library used to "easily" set up mappings between tables from different databases. Here's a small snippet:

        // Setup the data mappers
new DataMapBuilder<AimLoginContext, LivestockContext, UserDataMap, LivestockEntityTypes>(services)
    .UseSingleReference<User, Role>()
    .UseSingleValue<User, AlUserInfo>();

new DataMapBuilder<LivestockContext, LivestockContext, AdmmGroupMap, AdmuGroupEntityTypes>(services)
    .UseUserType<AdmuGroup>()
    .UseMapDatabase()
    .UseMultiReference<AdmuGroup, Critter>();

new DataMapBuilder<LivestockContext, AimLoginContext, AdmmGroupMap, AdmuGroupEntityTypes>(services)
    .UseMultiReference<AdmuGroup, User>();
    

AimLogin was supposed to be a login library that was going to be shared between multiple AIM-related websites. It was pretty trash though in all honesty. And besides, I found out about ASP Core's Identity stuff later on.

The final interesting thing about this project: It had a 'CustomScaffolder' tool which would generate all the CRUD '.cshtml' and controller files for every generic thing (critters; holdings; vehicles, etc.) reflected from the database schema. It was a complete and utter mess.

Version 3

Version 3 was the most complete version of FarmMaster, but was still far from being complete.

For version 3 I moved away from Bootstrap and instead used Fomantic UI instead. I also replaced most of the AJAX GETters with GraphQL, but there were still AJAX POST endpoints.

At this point I also transitioned over to code-first for EF Core, since I found it easier to model than with database-first. I had also moved away from SQL Server and instead started to use Postgres as I saw SQL Server as nothing but a limitation in comparison.

(Apologies for the low quality video - I don't want these videos to eat my bandwith allowance so they're compressed a decent amount!)

The issue with this version was that the code was still a complete and utter mess, both frontend and backend.

Backend-wise, this was my first time setting up GraphQL so it was mostly just a chaotic nightmare. For some reason ASP Core's claims weren't automatically populating either, so there were some ugly hacks there. The data access layer was also just flat out bad and buggy.

I just... don't want to talk about the frontend code at all. Let's just say, I learned the hard way why there are so many Javascript front-end frameworks, and it drove me to learn Vue which is a decision I will never regret. *cough* also each page had their own specific javascript that was often duplicated between other pages *cough*.

ASP Core uses a really trashy validation library by default, so I took it upon myself to make something slightly better/worse. The Javascript side of things worked well on its own, but I also added a C# integration for it.

You can see I put a moderate amount of effort to make the UX not completely awful. I was still far off the mark, but at least I tried!

This version had a rather buggy yet robust role system; GDPR friendly contact storage; Dynamic user-created forms for animal life events; Relatively decent animal search functions, and last but not least: GroupScript.

You'll see near the end of the video what GroupScript is, and I was definitely a bit optimistic about whether it was going to be useful or not, especially for the type of people who worked at AIM, but I thought it was cool at the time. Just don't ask why I implemented half of it as a stored procedure.

This project was what I'd call my "booster" project. This is when I started to really get comfortable with ASP Core, Javascript/Typescript, server management, etc. And from that point forward web development became much less stressful on my mind, and I could start to just bumble along and do whatever it is I wanted to get done.

Of course, there's a million different frameworks, paradigms, patterns, and concepts I still need to learn, but this is when web development started to open up for me.

Version 4

I eventually got sick of how brittle and unusable the codebase for Version 3 was, so I started work on Version 4.

With version 4 I learned more about how to use Webpack and Gulp; I started to use my own CSS (via SASS) instead of relying on a pre-made styling framework; I started to use ASP Core's features a bit more instead of hacking my own things on top of it, and just in general I was trying to use the tools given to me better instead of just writing my own dodgy versions.

This version made use of Vue, SASS, GraphQL, Typescript, and most interestingly: the backend code had a modular architecture.

You can see for yourself how the project was layed out, and while it wasn't the greatest, it was so much easier to work with than any of the previous versions.

Because I was using my own responsive SASS, as well as using Vue, the front end was easy to maintain, improve, and easy to keep consistant between pages.

Because I was using GraphQL for both querying and mutations, there was a single unified way on both the front end and back end (excluding form POST endpoints) to manipulate data.

This version had a core project which was the standard ASP Core loading point, but during loading it would then load up a list of predefined modules so they can inject their features into FarmMaster.

These modules were compiled into the final FarmMaster project, but technically with a bit of extra work FarmMaster would be able to dynamically load external plugins, which was one of the long-term goals I wanted to achieve.

Modules had to define and expose a class that implements the ModuleConfigurator base class:

        public abstract class ModuleConfigurator
{
    public abstract ModuleInfo Info { get; }
    public virtual void RegisterFeatureProviders(ApplicationPartManager parts) { }
    public virtual void RegisterNavMenuItems(NavMenu menu) { }
}
    

Modules could then do things such as: adding items onto the nav menu; adding onto the GraphQL schema; exposing an API for other modules to make use of (this is how the GraphQL stuff is done); registering their controllers, middleware, views, and so on. I also used ASP Core's ApplicationFeature API to provide even further configuration and support between modules and the core framework of FarmMaster.

Here is the AccountModule as an example:

        internal class AccountConfigureProvider : IApplicationFeatureProvider<ConfigureFeature>
{
    public void PopulateFeature(IEnumerable<ApplicationPart> parts, ConfigureFeature feature)
    {
        feature.ConfigurePipeline.Add(new AccountConfigurePipeline());
        feature.ConfigureServices.Add(new AccountConfigureServices());
    }
}

internal class AccountGraphQLProvider : IApplicationFeatureProvider<GraphQLFeature>
{
    public void PopulateFeature(IEnumerable<ApplicationPart> parts, GraphQLFeature feature)
    {
        feature.AddGraphQLPart<AccountQueries>();
    }
}

public class Module : ModuleConfigurator
{
    public override ModuleInfo Info => this._info;
    private readonly ModuleInfo _info = new ModuleInfo
    {
        Name = "AccountModule",
        LoadOrder = 0
    };

    public override void RegisterFeatureProviders(ApplicationPartManager parts)
    {
        parts.FeatureProviders.Add(new AccountConfigureProvider());
        parts.FeatureProviders.Add(new AccountGraphQLProvider());
    }
}
    

Conclusion

That's mostly it I guess. This all happened only within a year, and I even got an award for this project.

All my knowledge of Web dev, databases, server provisioning and management, etc. is all thanks to this project. So even though it's not being worked on anymore (due to me no longer being at AIM), it's an invaluable part of my development as a programmer.