GameForce, Part 4. Backend

Fedir Kryvyi
9 min readJul 13, 2023

In this article, I will try to explain how the backend of the solution was designed. Contrary to the previous article in this series this is the topic where I have quite a lot of expertise. But, what you have to understand is that it is way easier to design some specific feature in the scope of an already defined project, compared to designing something from scratch.

When working on project scope, I usually tend to think about features as self-sufficient (in a way) and deployable chunks of the project. But this time around I’ve decided to steer away from this approach. The thing is that I estimate that the balance between backend-related issues and frontend-related issues will heavily tip toward the frontend. And, admittedly, the backend for the whole solution should be fairly straightforward to do. And so I want to build the backend first and to make sure that it won’t need a lot of re-iteration in the future.

My usual approach (on the left) VS approach for this project (right)

There are some obvious disadvantages of course. With the regular approach, I would end up with an MVP at the end of the first milestone (or at least some prototype), while the approach that I’ve taken requires a lot of initial prep work done before any kind of result can be seen. And yet, I still think this was the right thing to do. I’ve already had to change a portion of the backend once I’ve realized that some of the features would be hard to implement on top of it, and I am glad that I didn’t have to modify the frontend alongside those changes.

In the beginning, I didn’t have a well-defined backlog (arguably, I still don’t have it). Well, I mean you can’t get a ready-to-go backlog out of thin air. The only thing that I had was the definition of a project, which sounded somewhat like this:

Once a pre-defined event happens in a system, the user is notified by the UI that pops up on a screen once conditions defined as achievement are met. Users can also go to a page that would show their achievements and a leaderboard with other users. Also, I want admins to have the ability to define custom achievements

Once the definition was clear to me. I’ve started dissecting into smaller parts that than could have been converted into issues and tasks in a backlog

Once a pre-defined event happens in a system [Backend logic is needed to signal the system], the user is notified by the UI that pops up [Some sort of custom UI] on a screen once conditions defined as achievement are met [The definition and end goal for an achievement has to be stored somehow]. Users can also go to a page that would show their achievements and a leaderboard with other users [More custom UI]. Also, I want admins to have the ability to define custom achievements [So achievement definitions has to be accessible by admin users as well as an ability to signal the system]

After this small mental exercise was done, it became clear that the main components would consist of:

  1. List of achievements in a form of a custom sObject or Custom Metadata type, that would include a set of predefined standard achievements (deployed with CMT or pre-populated with post-deployment script)
  2. LWC UI that would include a custom Lightning App as well as some form of pop-up or modal window that would be shown whenever a user reaches a specific achievement (this will be covered in Milestone 2)
  3. Some kind of a platform event, that would result in a pop-up being shown to the user.
  4. Junction sObject that would allow many-to-many relationships between User and Achievements sObject.

The one thing that I had some doubts about was the way to store achievement definitions. Basically, I had a tough choice between storing them as custom metadata types(CMT) and having the ability to deploy a standard list of achievements but losing the ability to have relationships with custom sObjects (which would also mean that I would have to write custom logic to mimic the thing that can be done with a simple master-detail relationship). On the other hand, using custom sObject would mean that I would need to develop a custom post-deployment script to populate standard list of achievements. In the end, I’ve decided to stick to the second option. As a result, the initial version of the backend looked somehing like this:

SOME IMPORTANT DISCLAIMERS:

1. I am not going to use the Salesforce naming schema to explain things, because there might be people reading this article who are not familiar with Salesforce at all, and for them, it will be confusing why everything is suffixed with “__c”.

2. I know how to create UML diagrams and flow charts, but this is not documentation. This article is meant to explain the general idea, and that means that sometime schemas won’t fit standards. Live with that, schema-purists

3. I won’t describe all the fields in the database, but only the ones that make sense for the logic that I am trying to explain. If you want to see the full database schema, you can go to the project documentation here

Achievements are defined in custom sObject that has a “Goal” field which is a numeric representation of achievement (e.g. “number of closed leads” or “amount on all owned opps” etc.). Progress towards the achievement is stored in junction sObject for each user, and it can be empty if there is no progress for a user at all. Whenever the value in the “Progress” field changes gets evaluated and if it is greater or equal to the value under the “Goal” field of the corresponding achievement — a platform event gets fired to signal the system that a pop-up has to be shown to the user:

I would also need to have some way to make sure that we don’t fire an event multiple times for the same thing, but that can be tracked by having a checkbox on a junction object, so I am not going to explain it here.

One question that still remained was how we would increase the value of the “Progress” field. Sure, we can just update the value with an Apex trigger whenever something happens in a system:

But, this would mean that in order to define a new custom achievement, the Salesforce admin would have to create a record in Achievement sObject, as well as define custom logic to insert or update records in AchievementByUser junction sObject. This logic would have to find and update the existing records, as well as create new ones whenever the record wasn’t found, and that feels like a bit too much for an admin to define using no-code tools in Salesforce (especially when you consider that they would have to do this for each new custom achievement). Instead, a better solution would be to have some sort of a gatekeeper, that would encapsulate complex logic from the user. And platform event is a perfect gatekeeper for this scenario:

Admins would still have to figure out when to throw the event, sure. But all the “find and update or create” kind of logic would be encapsulated from them inside of the platform event trigger handler.

Now, when we know how the “progress” field is going to be updated, the backend schema can be extended like this:

Pretty simple, right? Well, do you remember how at the beginning of this article, I said that I had found an issue that forced me to re-do the backend? The schema above shows the backend before that.

The thing that I’ve realized is that in a lot of cases, it makes sense to have achievements that have stages. For example, the achievement that the user gets when he had created 10 leads, 50 leads, and 100 leads. Those would be three different Achievement records, with different goals (10, 50, and 100), and as a result, we would have to store 3 corresponding records in AchievementByUser sObject. Once a new lead is created, we would have to update all three records at once, which is not impossible, but it is a clunky solution at best.

To make it better, I had to get rid of a junction object that connects User and Achievement sObject directly and replace it with another sObject that would store the statistics that would be tracked for each user (e.g. “number of closed leads”).

Then I added a relationship between Achievement sObject and Measurement sObject, which allowed me to group multiple achievements using the same measurement (e.g. “Lead creator LVL1”, “Lead creator LVL2”, “Lead creator LVL3” etc.)

Incrementing values under MeasurementByUser sObject would still rely on a platform event, to encapsulate complex logic. But admins wouldn’t have to think about achievements at all, instead, they would have to think about metrics that they would track for users (they could think of them as KPIs of sorts).

Throwing AchievementReached event to signal the system would remain almost the same, but instead of comparing the value in the “Goal” field with the value under the “Progress” field directly, the backend logic would have to get all the Achievements that are related to a single measurement and compare their “Goal” values one by one. (e.g. the user that has created 70 leads has already reached LVL1 and LVL2 achievements but not LVL3)

And while this might seem like a way more complicated solution compared to the one that I’ve origianly planned, I feel that it is way more robust and enables better way to configure achievements. Also, I would admit that there might better ways to design this, but I believe that “perfect” is the worst enemy of “good enough”, and this solution seems to be good enough for what I am planning to do next, and I don’t want to spend more time delaying the Milestone 2.

Hopefully, you’ve liked this article and maybe you even have some ideas on how this thing can be improved. I would love to hear them, so leave the comment wherever you can. Or, even better — create a GitHub issue for me in the repository. Contribution rules can be found here.

The next article will be dedicated to the topic that I’ve deliberately postponed since I didn’t have a lot of things to test. But I do now! And also I am hoping for others to contribute, so setting up a CI/CD is a must. So, stay tuned!

Part 5. Setting up Github Actions as CI/CD for Salesforce

Part 3. Pros Cons and Pitfalls of an open-source project

GameForce GitHub

--

--

Fedir Kryvyi

I am a Salesforce Developer with previous Dynamics 355 experience, and I am writing about my thoughts/discoveries about those two platforms or tech in general