Custom Field, Custom Object & Remote Object Data Architecture #1142
Replies: 4 comments 3 replies
-
The described proposal sounds great and very innovative to me ! Proposal for Apollo Schema Composition based on Object TypesGiven the complexity and multifaceted nature of our GraphQL architecture, I propose that we implement Apollo schema composition to improve our overall system's efficiency, maintainability, and scalability. Here are the primary reasons for this recommendation: Modular Schemas: In our monolithic application, we will segment our GraphQL schema into distinct modules based on object types: standardObject Schema: Handles the core entities and the default behavior. Unified Access Point: Apollo Gateway can combine these smaller schema segments into a single unified schema. This provides clients with a single endpoint, streamlining client development and reducing confusion. Each of them can also be versioned. Consolidated Business Logic: Business logic remains inside our single application, ensuring tight integration, especially when there's cross-talk between standard, custom, and remote objects. Schema Evolution: This approach provides us with the flexibility to modify and expand parts of our schema independently, making future changes less disruptive. Benefits: Clear separation of concerns, ease of deployment, and a singular endpoint for GraphQL requests. Considerations: Potential for resource bottlenecks and reduced scalability as demand grows ? Other thinks to take in consideration
|
Beta Was this translation helpful? Give feedback.
-
Instead of cross-posting, see my comment here: The essence is: pass current user data to the data source when performing remote operations sources |
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
Hi guys, great presentation of fields in general. I created a custom Object: Services, it comes with a a Default field: Name, however, if I want to bind 1 Opportunity to Many Services, I need to do so by their names, it then creates a conflict, I believe with the "Name" which is not unique? Here is an example Service, Name : Lawn mowing Any given opportunity can have any of those services structured as follows: Custom Field Service/Services Activities Account Status Name ==> duplicate key value violates unique constraint Quantity Price Creation date So the only filed I need as identifier in my Opp is the Name of the service, but when I try to bind it from my Opportunity (that has no name apparently) it creates a conflict with Name which is a concatenation of Firstname and Lastname I understand for people? But won't that be a systematic problem for every relationship if we have the name field "name" created by default with any custom object. Shouldn't we call it "CustomeObjectName" rather than "name" (but I suspected this was a label until I saw the exception)? I am having nevertheless the error below thrown and I can't bind them, why? I my case ServiceName? This is what happens if I try to target my Service object field called "name" from my custom field in Opportunity "Service". |
Beta Was this translation helpful? Give feedback.
-
/!\ THIS IS NOT UP TO DATE - WE WENT WITH 1SCHEMA / TENANT ARCHITECTURE IN THE END /!\
Custom Field, Custom Object & Remote Object Data Architecture
Owners: @Weiko @charlesBochet
Requirements
Definitions
Scenario
Our goal is to provide a unified GraphQL schema where users can query standards object, custom fields, custom objects and remote objects.
Let’s imagine the following scenario:
This corresponds to the following gql unified model:
High Level schema
We have a gateway that’s exposing a /graphql endpoint and a /meta endpoint.
Multitenancy
All databases except Remote datasources (obviously) are multi-tenant. The Gateway is also multi-tenant. (1) is met.
Note: If we have a big customer who wants to have complete isolation, we can still host a multi tenant system as a single tenant for this customer. This type of customer can also use self-hosting.
Note: ALL data models have a workspaceId. This enables data scoping, and later sharding.
Custom Field, Custom Object & Remote Object
Data structure is stored in Metadata tables (data_sources, object_metas, field_metas).
Values are stored in either standard objects, custom objects or remote datasource.
Custom Field on a standard object
This will be stored in the JSON customFields column.
Custom Object
This will be stored in a custom_objects table.
Remote Object
Remote objects are read from remote sources
Metadata update and Unified GraphQL schema
To add a remote datasource, a customField or a customObject, the workspaceMember can query the /meta API. No migration is performed on any database.
GQL cache would look like:
Querying the data
Let’s directly take a complex example of query:
This query goes through the QueryResolver:
Naive approach:
This nested logic should be handled in a nice way with graphql resolvers.
(This solves (2), (3), (4) requirements)
Data integrity
[To detail later]
[To detail later, seems overcomplicated and might not work]
Scalable
We can use horizontal sharding on workspace_id. Nothing hard here!
Performances discussion
We can get optimal performance on Standard objects and standard fields.
Standard fields vs custom fields
Custom fields are stored as JSON. As we don’t know the shape of this JSON we cannot index this JSON. This means that filtering or sorting by a custom field is slower. If the number of entity is reasonable (<100k), this should still be doable with acceptable response time for the user. Filtering is ~O(n)
Custom Objects
Custom object query performance should be similar as custom fields query performance. Join can be costly and should be avoided at read time
Remote Objects
As long as remote object foreignKey is indexed, we should have an acceptable performance for the user except for join.
Complex example: filter PipelineProgresses on Clients > Organization > DisplayName
Let’s imagine that our standard object PipelineProgress has a customObjectId targeting a client (custom object). We want to display the PP whose client has an organization with a name containing ‘Qo’
Query o=Organization by displayName: ~O(1). [ex: Org=1000 orgs match ‘Qo’]
Fetch c=Clients that have the corresponding organization ids ~O(n * Org) [ex: Cli=10000 clients match]
Fetch pp=PipelineProgresses on customObjectId target ~O(Cli)
This should done in a ~O(c * Org)
This should cover all usecases for standard pricing (<10.000 entities) without issue. For workspaces with more than that, we might want to create dedicated and optimzed index for these query (let the user manually create an index, or detect that we need to create it). We might also want to move these heavy customers to dedicated plans maybe on single tenant infra or with ES clusters.
Alternatives considered
Short term plan
This is a long term plan. To get there:
Later
Beta Was this translation helpful? Give feedback.
All reactions