Replies: 3 comments 2 replies
-
When we're talking about Events, are we referencing the fauna Events function? Or are we describing a definition within the data model, where we define create-only objects (Events) which can be resolved into Entities on the fly? If we're talking about the events fauna function, is there an example of an interesting kind of udf based entity resolution that showcases the need here? Resolving the entity to return the latest state of the entity would be the default behavior in any case (no need for any interesting resolution). |
Beta Was this translation helpful? Give feedback.
-
updated with vote-ready class diagram :) |
Beta Was this translation helpful? Give feedback.
-
Looks like I forgot to post a link here—part 1 of the RFC is open to votes! You can vote through this form, or Wednesday during the beginning of our weekly meeting at dualpower.app. |
Beta Was this translation helpful? Give feedback.
-
This RFC proposes an entity model compatible with our MVP wireframes, and an implementation for Fauna in which a client writes mostly-immutable Events to the database and reads back entities resolved from those Events.
Accessibility note: This draft contains a class diagram that will be made redundant with upcoming changes to the proposal text. The body text of this proposal will still make sense with a screen reader, but most of the entities in the model are stuck in the images until that next update.
Introduction
OTD (BSA Open Tech Development) is currently building out the MVP of Dual Power App based on information architecture and wireframes developed with @Manhattan-Hydraulics, and the biggest upcoming blocker to a working client is our data environment: the shape of that data that realizes the information architecture (entities), and the data store in which that information lives and with which clients & services interact (implementation).
This proposal addresses both entities and implementation, with the latter designed around the former. Through the architecture defined for the implementation, it also aims to consolidate work on Dual Power App's back end into the database by limiting the scopes of needs for infrastructure and for an API.
Sophisticated needs for our data model & data store include:
Draft
entityEvent
sEvent
sLong-term concerns include:
Editor's note: Hi, I'm Imogen, I work as an engineer + platform architect and write about transportation. I've been an OTD member since early 2020. I'm stewarding this proposal as an individual, and first presented it as a work-in-progress at the September 1, 2021 Open Design + Build. There are still some elements of this write-up that are missing, but the substance of the proposal will not change—I'm fleshing out the details then it's ready for a vote.
Prior work
Lucidchart
Part of this proposal is represented in a Lucidchart document, containing a class diagram of the entity model, a domain diagram of the data store implementation, and investigation into the considered approaches. The relevant elements are also embedded and linked throughout this proposal.
Entity model
This model builds on the Information Architecture by @Manhattan-Hydraulics, and aims to represent all of the functionality for our MVP, except where we need to determine the model as we build a feature (in which case, relations haev been determined but fields have not).
Entities
View in Lucidchart
All entities & descriptions:
Draft
: One version of a passage of rich text. Accepts suggestions, and may be considered "codified" when aPoll
passes. Belongs to aProposal
. Could support other parents in the future, forCircle
resources to be democratically updated on an ongoing basis.Changes since initial Information Architecture
Circle
: Org, Circle, and Area are now justCircle
.Draft
:Proposal
and other entities have their content edited with aDraft
, which provides revision history and version-specific approvals.Desire
: Desires used to be created withProposal
s, now they are addressed byProposal
s and can be amended withDraft
sEvent
s can be associated withPollOption
s to give them an approval status. This means an Action object is not necessary.Out of scope entities and relations
The class diagram for this proposal includes likely upcoming features that are out of scope for MVP, to ensure forward compatiibility. These are drawn into the diagram at a reduced opacity, and won't be fully fleshed out until after this proposal.
Location
management forCircle
s.Proposal
s resulting in new or updatedDocument
s,Desire
s,Engagement
s,Fund
s, andVoteModelType
s.Vote
to anotherUser
.CircleRole
: A means of delegating authority to aUser
. ACircleRole
and grants/revocations thereof would likely be bound toProposal
s and authority delegated by a differentCircleRole
.Federation
: A formation of manyCircle
s. AFederation
differs from a parentCircle
in that theCircle
is not "part of" theFederation
, but a participant, an can be active in manyFederation
s just as aUser
may be active in manyCircle
s.Watch
: Represents aUser
receiving updates about aUser | Circle
. This is similar to following on another social platform, but the language is explicitly selected to avoid a connotation of endorsement.Boost
: Represents aUser | Circle
supporting, elevating, or endorsing aUser | Circle
. This was conceived alongsideWatch
as an alternative to following, but could be generalized to a more broad idiom.Database implementation
This implementation part of this proposal implements the entity model in Fauna with a collection of
Event
s, using user-defined functions in the database to resolve entities at runtime (i.e. construct them from the event history, rather than saving their state to the database):Event
s simplify implementation of core features of the platform. This proposal focuses on feeds and the Proposal flow.Event
s eliminates concerns of state drift between entities and their history, and allows users granular, retroactively-effective privacy controls.View in Lucidchart
Fauna
There are a few different ways to use Fauna, but this proposal adopt the most common pattern:
Why Fauna?
Identity-based access
Authentication
Fauna can accept JSON Web Tokens from external identity providers, and grant one or more roles to those tokens. By creating an AccessProvider for Auth0 or another provider, we can safely consume user-specific tokens, associate them with internal
User
documents, and validate their permissions with predicate functions and UDFs.Access control
With identity-based access, there are two ways to enforce permissions in Fauna:
A provisional list of instances where access may be selectively granted is available to OTD members in Nimbus's 9/20 comment on the "Choosing a Data Store" thread.
Migrations
A "migration" consists of a replacement of the GraphQL schema and/or addition/replacement of UDFs, both of which have been validated in a test database. Literal migration of the data from an old format to the current format happens at runtime as documents are accessed, after the "migration" process
#83 provides two documented examples of the basic migration pattern, with step-by-step instructions for each.
Phased migrations without downtime are feasible, but this proposal advocates for a simple approach that accepts occasional late-night downtime;
Versioning for breaking changes
To accommodate versioned requests, a
version
argument may be passed to query or mutation and handled by its UDF. This should be adopted in the first breaking change after the MVP release, and a discussion will be needed on whether to version globally or per-resolver.User data controls
This proposal does not cover endorsement of user-defined visibility controls, retention policies, or bulk deletes functionality. If this proposal passes, the following approaches should be considered:
just-in-time migrations
, UDFs can be called during any transaction to enforce retention policies and visibility controls.Concerns with Fauna
Event
sAn
Event
is an additional entity representing a change to the internal state of Dual Power App. By making theEvent
a persistent entity and source of truth for other entities, we can accommodate our needs to subject state changes to the Proposal process and to resolve aUser
orCircle
's activity feed from a mixed set of entities and changes to entities.All Event types & descriptions (coming in next update):
Come back soon :)
Semi-immutability
With a collection of
Event
s capturing all changes to entities, there's no need to ever update anEvent
, with the exceptions of migrations and user-requested data removal. Given an uncorrupted collection ofEvent
s,Entity
state is replayable from the beginning of time.Entity resolution
Since an
Event
represents a change, it's not necessary to persist entities to the database. Instead, we can use each query's UDF to resolve (i.e. construct) entities on demand, based on the log of changes to that entity in theEvent
s collection, referencing the entity with the_id
of theEvent
that created the entity and reducing across the following relatedEvent
s until the current time. This grants a few benefits:Event
s are the source of truth, entity state will never drift.Event
s, the scope of possible problems that can break our data is confined to (a) improperly writtenEvent
mutation resolver UDFs and (b) unavoidable concerns about security controls.GraphQL schema
Our schema for this approach would differ from an entities-first schema in the following ways:
Event
would become embedded typesAlternatives to entity resolution in the database
Event
s, and instead create & mutate entities directly: With this approach basic CRUD operations would be easiest to scaffold. However, due to the openness of entities to mutations, more collection-level permissions would be required to enforce safe mutations (as opposed to index-level permissions and input validation). Additionally, our data model is sufficiently complex that additional schema or logic would be required to resolve things like an activity feed or document history anyway.Event
s, but persist entity state along with theEvent
s: This was initially the preferred option in this proposal. Basic entity queries would be free with GraphQL schema upload, and only mutations would require custom logic in UDFs. However, it was raised in the 9/1 Open Design + Build meeting that a buggy mutation UDF slipping through the crack would cause destructive changes to our data, the approach of resolving entities would mean no UDFs need to update data unless the Events collection is migrated. There's also a possibility of state drift within the database itself, between the event log and the entities.Event
s, and resolve entity state on the client instead of in the database: This option was proposed as a means of reducing the number and size of client-db transactions and enabling message passing between clients. This option seems infeasible, considering the size of initial load times would increase over time unless we maintained a second GraphQL service in front of Fauna with a cache of entity state.See the end of the "Data store implementation" section of the Lucidchart for a breakdown of pros & cons with each approach.
Concerns with entity resolution
Next steps
Upcoming changes
There are a bunch of "TODO" items throughout this RFC, mostly amounting to:
These are required for the second vote on database implementation, but not for the first on entity model.
Votes
Votes on this proposal can be cast asynchronously or at an Open Design + Build meeting, which happens every other Wednesday. Dates and instructions for the votes will be posted about a week before voting starts, and voting will be open for a week.
Beta Was this translation helpful? Give feedback.
All reactions