Facts can represent steps in a workflow.
For each step, the fact captures all of the context that is relevant to that particular decision.
It refers back to prior workflow steps to create chains of operations.
The creator of a blog can write posts in private.
Once they are satisfied, they can publish the post.
They will record this decision to publish using a new fact.
This fact will capture the specific title and content that the author chose to publish.
Finally, it will refer back to prior published versions so that they can be replaced.
When the user makes the decision to publish the post, we capture that as a fact.
var post1Publish0 =await jinagaClient.Fact(newPostPublish(post1, postTitle1, postContent1,[]));
jinagaClient.RenderFacts(post1Publish0)
To render the site, create a specification that displays only the published posts.
Here you have to be careful.
You might at first want to write a specification that selects predecessors and properties of predecessors like this:
var publishedPostsInSiteIncorrect = Given<Site>.Match((site, facts)=>from publish in facts.OfType<PostPublish>()wherepublish.post.site == site
where!facts.Any<PostPublish>(next => next.prior.Contains(publish))selectnew{// Get the post predecessor from the publish fact
Post = publish.post,// Get the value of the title predecessor
Title = publish.title.value,// Get the value of the content predecessor
Content = publish.content.markdown
});
Error: Jinaga.SpecificationException: Cannot select post directly. Give the fact a label first.
at Jinaga.Repository.SpecificationProcessor.ProcessProjection...
That is an invalid specification.
The problem is that we are trying to select a predecessor directly in the projection.
We can't know for sure that just one predecessor exists.
The model may have changed over time.
That predecessor might have been added, or it might have changed from an array to a single value.
Jinaga does not make any assumptions about the evolution of the model.
Instead, you have to explicitly give each predecessor a label.
Then, you can select fields of those predecessors in the projection.
var publishedPostsInSite = Given<Site>.Match((site, facts)=>from publish in facts.OfType<PostPublish>()wherepublish.post.site == site
where!facts.Any<PostPublish>(next => next.prior.Contains(publish))from post in facts.OfType<Post>()wherepost== publish.post
from title in facts.OfType<PostTitle>()wheretitle== publish.title
from content in facts.OfType<PostContent>()wherecontent== publish.content
selectnew{
Post = post,
Title = title.value,
Content = content.markdown
});var publishedPosts =await jinagaClient.Query(publishedPostsInSite, site);
publishedPosts
index
value
0
{ Post = Post { ... }, Title = Interesting Facts, Content = Let me ... }
Post
Post { site = Site { creator = Jinaga.User, createdAt = 5/18/2024 5:54:05 PM }, author = Jinaga.User, createdAt = 5/18/2024 5:54:09 PM }
If the publish fact does not have a title or content predecessor, then this specification will not return the result.
Furthermore, if it has multiple title or content predecessors -- for example if the model previously captured arrays instead of single values -- then it would return one result per combination.
This might not be your desired result.
An alternate specification might explicitly say that it wants one result per published post, no matter how many title or content facts are present.
It then gets the list of titles and contents within the projection.
If multiples exist, then they will be listed in these collections.
var publishedPostsInSiteAlternate = Given<Site>.Match((site, facts)=>from publish in facts.OfType<PostPublish>()wherepublish.post.site == site
where!facts.Any<PostPublish>(next => next.prior.Contains(publish))from post in facts.OfType<Post>()wherepost== publish.post
selectnew{
Post = post,
Titles =from title in facts.OfType<PostTitle>()wheretitle== publish.title
select title.value,
Contents =from content in facts.OfType<PostContent>()wherecontent== publish.content
select content.markdown
});var publishedPostsAlternate =await jinagaClient.Query(publishedPostsInSiteAlternate, site);
publishedPostsAlternate
index
value
0
{ Post = Post { ... }, Titles = [ Interesting Facts ], Contents = [ Let me ... ] }
Post
Post { site = Site { creator = Jinaga.User, createdAt = 5/18/2024 5:54:05 PM }, author = Jinaga.User, createdAt = 5/18/2024 5:54:09 PM }
Jinaga does not assume that you want one form vs the other.
You must explicitly label the facts that you want to be present in the projection.
From there, you can define nested projections to pick out related facts.