Workflow often includes more than a single step. To represent additional steps in a workflow, define more fact types. Each step refers back to the previous step in the workflow.
After a post is published, it can be rescinded. The author might want to take it down for a time, or permanently remove it. Represent this with another fact type.
[FactType("Blog.Post.Rescind")]
public record PostRescind(PostPublish publish, DateTime rescindedAt) { }
Renderer.RenderTypes(typeof(PostRescind))
Modify the specifications that should take these workflow steps into account. For example, we don't want to display rescinded posts on the site.
publishedPostsInSite = Given<Site>.Match((site, facts) =>
from publish in facts.OfType<PostPublish>()
where publish.post.site == site
where !facts.Any<PostPublish>(next => next.prior.Contains(publish))
// Filter out the rescinded posts
where !facts.Any<PostRescind>(pr => pr.publish == publish)
from post in facts.OfType<Post>()
where post == publish.post
from title in facts.OfType<PostTitle>()
where title == publish.title
from content in facts.OfType<PostContent>()
where content == publish.content
select new
{
Post = post,
Title = title.value,
Content = content.markdown
});
Now if the author rescinds their publication, the post will no longer appear on the site.
var rescindPost1Publish1 = await jinagaClient.Fact(new PostRescind(
post1Publish1, DateTime.UtcNow));
publishedPosts = await jinagaClient.Query(publishedPostsInSite, site);
publishedPostsViewModel = publishedPosts.Select(p => new
{
Title = p.Title,
Content = p.Content
});
publishedPostsViewModel
(empty)
The next step in the workflow might be to re-publish a rescinded post. As you might expect, we represent that with another fact.
[FactType("Blog.Post.Republish")]
public record PostRepublish(PostRescind rescind) { }
Renderer.RenderTypes(typeof(PostRepublish))
Then we update the specification to ignore a rescind fact if it has been republished.
publishedPostsInSite = Given<Site>.Match((site, facts) =>
from publish in facts.OfType<PostPublish>()
where publish.post.site == site
where !facts.Any<PostPublish>(next => next.prior.Contains(publish))
where !facts.Any<PostRescind>(pr => pr.publish == publish &&
// Include only the rescinds that have not been republished
!facts.Any<PostRepublish>(rp => rp.rescind == pr))
from post in facts.OfType<Post>()
where post == publish.post
from title in facts.OfType<PostTitle>()
where title == publish.title
from content in facts.OfType<PostContent>()
where content == publish.content
select new
{
Post = post,
Title = title.value,
Content = content.markdown
});
And with this, the author can re-publish their rescinded post.
var republishPost1Rescind = await jinagaClient.Fact(new PostRepublish(
rescindPost1Publish1));
publishedPosts = await jinagaClient.Query(publishedPostsInSite, site);
publishedPostsViewModel = publishedPosts.Select(p => new
{
Title = p.Title,
Content = p.Content
});
publishedPostsViewModel
index | value | ||||
---|---|---|---|---|---|
0 |
|
Title | Interesting Facts |
Content | Let me tell you some more. |
The decision of which workflow facts have timestamps is very deliberate. It must be considered carefully. A timestamp is added to a workflow fact only when it is needed to differentiate it from other facts.
Let's first consider the PostPublish
fact.
It has no timestamp.
The reason is that it contains a prior
array that places it in history relative to other PostPublish
facts.
This is enough to distinguish it from other publication steps.
Then let's look at the PostRescind
fact.
This one has a timestamp.
Without this differentiator, there could be only one PostRescind
for a given PostPublish
.
An author could rescind a post and then re-publish it.
After that, they could not express the desire to rescind it again.
That second rescind would be indistinguishable from the first.
Finally, let's look at PostRepublish
.
This one does not have a timestamp.
It does not need one because it is the last step in the workflow.
If an author wishes to rescind the publication again, they could create another PostRescind
fact.
There is no need to further qualify a re-publish fact, and therefore no need to differentiate it from any other re-publication of the same rescinded post.