Monday, November 12, 2018

Revisions and Immutability

Here's a brief post. I'm not sure how to start it. It's one of those "why didn't I think of that" moments while reviewing some existing code. Due to NDAs, I cannot share the actual code. It has something to do with handling revisions. The closest thing I can relate to is how WordPress (WP) handles blog posts and revisions.

In WP, the wp_insert_post function inserts or updates a post. It checks the ID field to determine if it will carry out an INSERT or an UPDATE. If the post is being updated, it checks if changes were made. If so, a revision is saved. A limit for the number of revisions to keep can be set. If so, the oldest ones are deleted.

This sounds like something that can be modeled as a rich domain entity. Here's a first try.

@Entity
... class Post {
    @Id @GeneratedValue ... id;
    ... name;
    ... title;
    ... content;
    ... excerpt;
    ... status; // e.g. 'draft', 'publish', 'inherit'
    ... type; // e.g. 'post', 'revision'
    @OneToMany @JoinColumn(name="parent_post_id") ... List<Post> revisions;
    ...
    // setters and getters
}
Post post = new Post();
post.setTitle("Lorem Ipsum");
post.setContent("...");
// save post
...
post = // retrieve existing post for updates
post.setContent("..."); // how can we ensure that revision is created?

In the first try, the setter methods pose a challenge to ensuring that a revision is created when the post is updated. Let's give it another try. Here's our second try.

// Immutable class
@Embeddable
... class PostData {
    ... title;
    ... content;
    ... excerpt;
    // getters only
    ... getTitle() { return title; }
    ... getContent() { return content; }
    ... getExcerpt() { return excerpt; }
    // equals() method to compare with another post data
    // to see if there are changes
}

@Entity
... class Post {
    @Id @GeneratedValue ... id;
    ... name; // for a revision, will contain parent ID and revision #
    @Embedded ... PostData postData; // read-only
    ... status; // e.g. 'draft', 'published', 'inherit'
    ... type; // e.g. 'post', 'revision'
    @OneToMany @JoinColumn(name="parent_post_id") ... List<Post> revisions;
    ...
    ... getTitle() { return this.postData.getTitle(); }
    ... getContent() { return this.postData.getContent(); }
    ... getExcerpt() { return this.postData.getExcerpt(); }
    ... getName() { return name; }
}

This is when I got my "why didn't I think of that" moment!

Note how we encapsulated the post data into its own type — PostData. It is immutable. This makes it possible to ensure that a revision is created when the post is updated.

PostData postData = new PostData("Lorem Ipsum", "...", "...");
Post post = new Post(postData);
// save post
...
post = // retrieve existing post for updates
// post.setContent("..."); // not possible
post.updateData(new PostData("...", "...", "...")); // ensure that revision is created

And here's how we create revisions.

@Entity
... class Post {
    ...
    @Embedded ... PostData postData; // read-only
    ...
    @OneToMany @JoinColumn(name="parent_post_id") ... List<Post> revisions;
    ...
    public Post(PostData postData) {
        this(postData, null);
    }
    /* package private */ Post(PostData postData, Post parent) {
        if (postData == null) {
            throw new IllegalArgumentException(...);
        }
        this.postData = postData;
        if (parent == null) {
            this.type = "post";
            this.status = "draft";
            this.name = null;
            this.revisions = new ArrayList<>();
        } else {
            this.type = "revision";
            this.status = "inherit";
            this.name = "" + parent.getId() + "-revision" + (parent.getRevisionsCount() + 1);
            this.revisions = null;
        }
        ...
    }
    ...
    ... void updateData(PostData newPostData) {
        if (this.postData.equals(newPostData)) {
            // no changes, no revisions added
            return;
        }
        ...
        // creates a revision
        PostData beforePostData = this.postData;
        this.revisions.add(0, new Post(beforePostData, this));
        // store latest changes
        this.postData = newPostData;
        // limit to number of revisions to keep
        if (this.revisions.size() > ...) {
            // delete the excess ones
            for (...) {
                this.revisions.remove(this.revisions.size() - 1);
            }
        }
        ...
    }
    ...
}

Like I said, this one is a brief post. Let me know in the comments below if it's something you've seen before, or, just like me, it gave you a "why didn't I think of that" moment.

7 comments:

  1. Purchasing YouTube subscribers through Paytm is a savvy strategy for creators looking to quickly increase their channel's audience. This method offers an immediate uplift in subscriber numbers, making your channel more appealing to potential viewers at first glance. The use of Paytm, a widely trusted and secure payment platform, ensures that transactions are smooth and hassle-free, allowing creators to focus on producing engaging content. With a higher subscriber count, channels become more visible on YouTube, enhancing their chances of being recommended by the platform's algorithm. This strategic move not only boosts the perceived popularity of your channel but also lays a strong foundation for organic growth. It's an efficient way to kickstart your YouTube presence, drawing more viewers and establishing credibility in the competitive digital space. This approach is particularly beneficial for new creators aiming to surpass the steep growth curve and make a lasting impact on their audience.
    https://www.buyyoutubesubscribers.in/

    ReplyDelete
  2. In Delhi, Lasik eye surgery is renowned for its precision and effectiveness, offering a beacon of hope to individuals plagued by vision imperfections. The city's medical infrastructure is at the forefront, featuring advanced laser technologies that ensure the utmost accuracy during the corrective procedure. Patients from across the globe are drawn to Delhi, not only for the high caliber of surgical expertise but also for the affordability of the treatments compared to Western countries. Each surgical session is succinct, with the promise of minimal discomfort and a rapid return to normalcy. Success stories abound, with countless patients attesting to a drastic enhancement in their quality of life post-surgery. The comprehensive aftercare provided by Delhi's medical specialists further underscores the commitment to patient welfare and satisfaction. Indeed, for many, Lasik surgery in Delhi is not just a medical procedure but a pathway to newfound confidence and a life free from the constraints of corrective eyewear.
    https://www.visualaidscentre.com/

    ReplyDelete
  3. Buying YouTube views is a tactical approach taken by content creators aiming to quickly elevate the visibility and engagement of their YouTube videos. This strategy revolves around purchasing views to artificially increase the video's view count in the short term, making the content appear more popular and enticing to potential organic viewers. When done judiciously, this method can trigger YouTube's algorithm to favor the video, leading to increased visibility across the platform and, subsequently, more genuine engagement. Selecting a reputable service that guarantees views from real, active users is vital to ensure compliance with YouTube’s stringent policies. Though this tactic can provide an immediate boost, it is most effective when used as a component of a broader, quality-focused content strategy aimed at fostering long-term growth and engagement. Ultimately, purchasing YouTube views can be a quick way to enhance visibility but requires a strategic approach to truly benefit the channel's overall performance.
    https://www.buyyoutubeviewsindia.in/

    ReplyDelete
  4. In India, cheap web hosting is revolutionizing how individuals and small enterprises approach the idea of establishing an online presence. By offering services at an affordable price point, these hosting providers are breaking down financial barriers that traditionally hindered digital aspirations. Features such as free domain registration, substantial storage, and ample bandwidth, paired with impressive uptime statistics, ensure that websites are efficient, reliable, and continuously accessible to the global audience. Furthermore, the support of dedicated customer service teams ensures that any technical hurdles are swiftly overcome, enhancing the overall web hosting experience. This democratization of web hosting is not just a trend but a significant movement towards inclusive digital empowerment, enabling a multitude of dreams to find their expression in the vast online landscape.
    https://onohosting.com/

    ReplyDelete
  5. Nursing jobs in Singapore are at the heart of its globally recognized healthcare sector, offering an enriching environment for nurses passionate about making a difference. In this vibrant city-state, nurses are given the opportunity to work in a high-tech, innovative healthcare system, which is continuously evolving. The diversity of roles available allows for specialization in areas such as critical care, gerontology, and community health, amongst others. These positions are supported by a culture of lifelong learning, with numerous professional development opportunities. Competitive compensation, comprehensive benefits, and the chance to work in a multicultural and dynamic environment further highlight the appeal of nursing careers in Singapore. Nurses not only contribute to the well-being of patients but also have the unique opportunity to be part of Singapore's commitment to excellence in healthcare.
    https://dynamichealthstaff.com/nursing-jobs-in-singapore-for-indian-nurses

    ReplyDelete