Explain Combining Domain-Driven Design and Databases

Some time in the past, we defined how to design software in a clean architecture way. At the moment, though we offered the design and implementation of the area objects and likewise applied the unit assessments, we didn’t describe combine with the database.

Due to this fact, this text will current one other instance of how domain-driven design and database will be put collectively. Subsequent, we’ll present a real-world design of a typical street-level gashapon retailer with a MySQL database.



Person Tales

As we have carried out earlier than, we begin by describing the person story, and we perceive our wants by way of that story.

  • There will likely be quite a lot of machines, every with completely different combos of things.
  • With the intention to permit “beneficiant” prospects to purchase every little thing without delay, we provide the choice to attract quite a lot of gadgets without delay.
  • Once we run out of things, we have to refill them instantly.
  • If an individual who attracts quite a lot of gadgets at one time runs out of things, we’ll refill the gadgets instantly in order that they will proceed to attract.

Such a narrative is definitely a situation that occurs in each gashapon retailer, and after a transparent description, we’ll know construct the area mannequin.

Usually talking, we might want to mannequin a gashapon machine and a gacha entity.



Use Circumstances

Then, we outline extra exact use circumstances based mostly on person tales. Not like a narrative, the use case will clearly describe what occurred and the way the system ought to react.

  • As a result of that is a web-based gashapon retailer, it’s potential for a number of folks to attract on the similar time. However similar to the bodily machine, everybody should attract order.
  • Within the technique of refilling the gacha, the person shouldn’t be allowed to attract till all of the gacha have been refilled.
  • When A and B every draw 50 on the similar time, however there are solely 70 within the machine, one in all them will get the 50 within the batch, whereas the opposite will draw 20 first, after which look ahead to the refill earlier than drawing 30.

With the use case, we are able to both draw a flowchart or write a course of based mostly on it. On this instance, I selected to jot down the method in Python as follows.

def draw(n, machine):
    gachas = machine.pop(n)

    if len(gachas) < n:
        machine.refill()
        gachas += draw(n - len(gachas), machine)

    return gachas
Enter fullscreen mode

Exit fullscreen mode

Within the person story, we talked about that we’ll mannequin a gashapon machine and a gacha. So the machine on this instance code is the gashapon machine, which offers each pop and refill strategies. However, gachas are an inventory with gacha in it.

Earlier than we go any additional, there may be one crucial factor that have to be acknowledged. Each the pop and refill strategies have to be atomic. To keep away from racing circumstances, each strategies have to be atomic and can’t be preempted, whereas there will likely be a number of customers drawing concurrently.



Database Modeling

We have already got machine and gacha, and these two objects are quite simple for builders who’re aware of object-oriented programming, however how ought to they be built-in with the database?

As talked about within the guide Patterns of Enterprise Software Structure, there are three choices to explain the area logic on the database.

  1. Transaction Script
  2. Desk Module
  3. Area Mannequin

The guide explains the professionals and cons of those three selections individually, and in my expertise, I choose to make use of Desk Module. The reason being, the database is a standalone part, and to the applying, the database is definitely a Singleton. To have the ability to management concurrent entry on this Singleton, it’s essential to have a single, unified and public interface.

With Transaction Script, entry to the database is unfold all around the supply code, making it virtually not possible to handle when the applying grows bigger. However, Area Mannequin is just too advanced, because it creates a particular occasion for every row of the desk, which may be very difficult when it comes to implementation atomic operations. So I selected the compromise Desk Module as the general public interface to work together with the database.

Take the above machine for example.

Since we’ve completed constructing the area object, let’s outline the schema of the desk GachaTable.

gacha_seq machine_id gacha_id is_drawn
1 1 jurassic-park-t-rex true
2 1 jurassic-park-t-rex false
3 1 jurassic-park-velociraptor false
4 2 ice-age-mammoth false
5 2 ice-age-mammoth false

Within the desk, we are able to see there are two machines, one with the theme Jurassic Park and the opposite Ice Age, each of which have two particular person gachas. The Jurassic Park machine appears like three, however one has really been drawn already.

Gacha‘s area mannequin is simple, which is gacha_id or extra detailed, which often is the theme plus gadgets, comparable to: Jurassic Park and T-Rex.

A extra attention-grabbing matter to debate is machine. machine must outline a number of attributes. First, it ought to be capable to specify an id within the constructor, and second, there are two atomic strategies, pop and refill. We’ll concentrate on these two strategies within the following sections.



Atomic Pop

It isn’t troublesome to implement an atomic pop: first type by sequence quantity, then take out the primary n rows, and eventually set is_drawn to true. Let’s take the Jurassic Park machine for example.

START TRANSACTION;
SELECT * FROM GachaTable WHERE machine_id = 1 AND is_drawn = false ORDER BY gacha_seq LIMIT n FOR UPDATE;
UPDATE GachaTable SET is_drawn = true WHERE gacha_seq IN ${resultSeqs};
COMMIT;
Enter fullscreen mode

Exit fullscreen mode

As talked about in my previous article, to be able to keep away from misplaced updates, MySQL has three approaches. On this instance, to realize atomic updates, it’s best so as to add FOR UPDATE to the tip of SELECT to preempt these rows.

When the replace is full, the results of SELECT will be wrapped into Gacha cases and returned. This fashion the caller will be capable to get the gachas drawn and know what number of have been drawn.



Atomic Refill

One other atomic technique is refill. It’s easy to not get interrupted in the midst of the fill course of. As a result of MySQL transactions are usually not learn by the remainder of purchasers till COMMIT below the Repeatable Learn situation.

START TRANSACTION;
for gacha in newGachaPackage():
    INSERT INTO GachaTable VALUES ${gacha};

COMMIT;
Enter fullscreen mode

Exit fullscreen mode

Is that every one? No, probably not.

This drawback happens when each customers draw n, however there are usually not sufficient n gachas to attract.

Image description

The sequential diagram above exhibits when A and B draw on the similar time, A will draw r gachas and B won’t, as we anticipated. Nevertheless, each A and B will refill collectively, leading to the identical batch of gachas being crammed twice.

Often, this doesn’t trigger any main issues. As a result of we prepare the pop by sequence quantity, we are able to assure that the second batch will likely be drawn solely after the primary batch is drained. But when we need to change the merchandise, then the brand new gadgets will likely be launched later than we anticipated.

However, two persons are refilling on the similar time so the redundancy turns into twice as a lot, and if the system has a really massive variety of simultaneous customers, then the redundancy could develop into a number of occasions as a lot and take up quite a lot of assets.

The best way to resolve this drawback? Within the article Solving Phantom Reads in MySQL, it’s talked about that we are able to materialize conflicts. In different phrases, we add an exterior synchronization mechanism to mediate all concurrent customers.

On this instance, we are able to add a brand new desk, MachineTable.

machine_id machine_model
1 APTX4869
2 APTX4870

This desk additionally permits the unique machine_id of GachaTable to have a further international key reference goal. Once we do refill, we’ve to lock this machine first earlier than we are able to replace it.

START TRANSACTION;
SELECT * FROM MachineTable WHERE machine_id = 1 FOR UPDATE;
SELECT COUNT(*) FROM GachaTable WHERE machine_id = 1 AND is_drawn = false;
if !cnt:
    for gacha in newGachaPackage():
        INSERT INTO GachaTable VALUES ${gacha};

COMMIT;
Enter fullscreen mode

Exit fullscreen mode

At first, we purchase the unique lock, then we re-confirm whether or not GachaTable wants refill, and eventually, we really insert the information into it. If it’s not reconfirmed, then it’s nonetheless potential to repeat refill.

Listed below are a couple of prolonged discussions.

  1. Why do we want a further MachineTable? Cannot we lock the unique GachaTable? On account of phantom reads, MySQL’s Repeatable Learn can’t keep away from write skew from phantom reads within the case of latest information. The detailed course of is defined in my previous article.
  2. When locking MachineTable, do not you’ll want to lock GachaTable when getting the depend from GachaTable? Truly, it’s not obligatory. As a result of it should enter the refill course of, it have to be as a result of the gachas have been drawn and everyone seems to be ready for the refill, so don’t fret concerning the pop.



Conclusion

On this article, we clarify the problems to be thought-about when combining a domain-driven design with a database design by way of a real-world instance.

The ultimate end result will include two objects, Gacha and Machine, and the database can even include two tables, GachaTable and MachineTable. All strategies inside Machine are atomic in nature.

As we described within the design steps earlier than, we have to first outline the proper person tales and use circumstances, then begin the modeling, and eventually the implementation. Not like regular software implementations, the database exists as a big Singleton, so we have to higher combine the database design into our area design as properly.

With the intention to decrease the impression of the database on the general design, it’s essential to construct the area mannequin accurately. After all, on this article we undertake the Desk Module method to area design, which has its benefits and downsides.

The benefit is by utilizing the Machine area mannequin, we’re capable of simulate the true look of a gashapon machine and supply a typical interface for all customers to synchronize their processing. By encapsulating the gashapon habits into Machine, future gashapon extensions will be simply carried out. All of the operations of GachaTable and MachineTable can even be managed by a single object.

The draw back is that Machine really comprises the gashapon machine and the gacha desk inside, which is just too tough for a strict object-oriented faith. When extra persons are concerned within the challenge, everybody’s understanding of the objects and the information tables begins to diverge, resulting in a design collapse. For a big group, the Desk Module has its scope, and higher cross-departmental collaboration depends on full documentation and design evaluations, which might have an effect on everybody’s productiveness.

On this article we don’t take an excessive amount of time to clarify the design course of, however relatively concentrate on combine the database design within the area objects. If you wish to be taught extra concerning the particulars talked about on this article, I’ve listed my earlier articles under.

Add a Comment

Your email address will not be published. Required fields are marked *