-
Notifications
You must be signed in to change notification settings - Fork 361
Notes on V3 Architecture
This article is about code design/ architecture for the v3 API.
For API design, see the API styleguide.
For Asynch/background jobs see: v3 asynchronous endpoint flow
The v2 API was architected to allow rapid generation of API endpoints by a small, dedicated team of developers. Now that Cloud Controller is a mature, open-source project with hundreds of contributors throughout the world, these architectural decisions have resulted in a few issues:
- The framework that v2 is build on is powerful and semi-flexible, but is not maintained as a stand-alone tool so it is not used by any other projects or documented for common use. This makes it difficult for new developers and open-source contributors to learn its numerous eccentricities.
- V2 endpoints are largely auto-generated, so it can be difficult to keep track of what endpoints exist. It can also be difficult to build endpoints that have custom behavior, not supported by the framework.
- V2's reliance on model hooks results in a distribution of business logic across multiple units. This has a number of implications. First, it can be difficult for developers to follow and modify user workflows through the various hooks. Second, the hooks can result in unexpected, emergent behaviors for seemingly unrelated changes. Third, testing user workflows is difficult outside of top-level API tests.
The code design of the v3 api is a direct response to these issues. For example, the v3 API:
- Uses several Rails libraries instead of a custom framework
- Favors explicit vs generalized or meta-programmed code
- Organizes business logic around user workflows (actions) instead of domain objects
Message objects run superficial validations of user input. For example, validate JSON structure, check for unexpected keys, and verify types of parameters/ data. They also serve as a value object to be used by subsequent objects in the request cycle.
- Run validations on the request that do not depend on database state
- Provide data from request to later collaborators in the system
The fetcher object’s responsibility is to get record(s) from the database. It also handles filtering based on user input. Fetchers are only used for endpoints with filtering or other complex query logic.
- Retrieve records from the database
- Apply pagination and filters
Action objects contain most of the business logic and typically implement user-facing workflows. Common examples include creation, deletion, or updating of a resource. Action objects make it easier to understand, test, and modify the v3 API's behavior.
Action objects may or may not be shared between v2 and v3 endpoints. Actions may be composed for more complex endpoints.
- Implement context-independent business logic
- Collaborate with other actions to build compound workflows
The presenter object handle rendering the response for the user’s request. It implements a to_hash
method that when supplied to the render method in ActionController, results in rendering JSON.
- Structure response data
- Make superficial presentation changes to the data
- Pagination
- Include links to related endpoints
Models that are only exposed on the v3 api have very few responsibilities. They model associations between resources, provide convenience methods for accessing their data, and have basic validation to detect invalid states.
- Write/read/update the database
- Provide convenience accessors
- Manage associations between resources
- Validations based on database state
- Rarely: Use model hooks to update external systems when states change
Controllers should coordinate between other objects, but contain little to no logic of their own. Controllers should be a gateway to API-agnostic behavior, and not implement any of it themselves.
For a standard controller, the request will go through 5 layers of scrutiny:
- Middleware will make sure that the user has a valid UAA token
- A Message will make sure that the request body is syntactically valid
- Permissions will make sure the user has access to the resources they are trying to act on. This must be after the message, because permissions are currently tied to orgs/spaces, which are often specified in request bodies. This check must depend on only the user's auth info and the space/org of the resource in question (no business logic).
- The action will make sure the request conforms to any business logic validations (no creating paperclips on Tuesdays!)
- The model/database schema will run validations that depend on database state (uniqueness validations). This is important to prevent/narrow race conditions around database state.
- Collaborate with Message and return any errors
- Collaborate with Permissions and return any errors
- Collaborate with Fetchers
- Collaborate with Actions and return any errors
- Collaborate with Presenters and render their results
Responsible for determining if a user has access to a give resource based on their roles and scopes.
- Actions taking messages as inputs reduces the portability of actions. If you want to use them outside of a v3 controller, you must create a message object by hand. Maybe actions should not take messages as arguments?
- It is currently ambiguous how much Messages care about structure of the request. They are de-facto responsible for parsing requests, but then also implement structure-agnostic data validations.
- Code examples
-
Pipelines
-
Contributing
- Tips and Tricks
- Cloud Controller API v3 Style Guide
- Playbooks
- Development configuration
- Testing
-
Architectural Details
-
CC Resources
- Apps
- Audit Events
- Deployments
- Labels
- Services
- Sidecars
-
Dependencies
-
Troubleshooting
- Ruby Console Script to Find Fields that Cannot Be Decrypted
- Logging database queries in unit tests
- Inspecting blobstore cc resources and cc packages(webdav)
- How to Use USR1 Trap for Diagnostics
- How to Perf: Finding and Fixing Bottlenecks
- How to get access to mysql database
- How To Get a Ruby Heap Dumps & GC Stats from CC
- How to curl v4 internal endpoints with mtls
- How to access Bosh Director console and restore an outdated Cloud Config
- Analyzing Cloud Controller's NGINX logs using the toplogs script
-
k8s
-
Archive