Skip to content

Application Structure

Banjo Fox edited this page Nov 27, 2018 · 8 revisions

So here`s how the app is structured right now.

  • The application stores data in a PostGRE SQL Database

  • There's the models, which are types that represent data in the database, and have associated logic for interacting with that data. This is the aardwolf-models crate.

  • There's operations. Operations are a higher-level view of database interactions. They take input, do some DB stuff, and provide output. Operations are located in: aardwolf-types/src/operations. Operations shouldnt make dieselqueries directly, it should use abstractions provided by theaardwolf-models` crate to get things done. Sometimes, this means an operation is only one line long.

  • Operations implement a trait called DbAction, which is defined in aardwolf-types/src/traits.rs. This provides a generic interface to an operation. Operations also implement a trait called Wrapped, but now that Im thinking about it, it might make more sense to include the Wrappedfunctionality in theDbActiontrait.Wrappedprovides a methodwrapthat wraps aDbActiontype in aDbActionWrapper` struct for easier generic interaction.

  • There's forms. Forms are types that represent data that can come in from the user. There are forms for signing up, logging in, etc. these are located in aardwolf-types/src/forms

  • Form Validation traits Forms implement a trait called Validate. Validate transforms a Form into a ValidatedForm, possibly producing a FormError. Forms also implement Wrapped, which provides a method wrap that wraps a Validate type in a ValidateWrapper struct for easier generic interaction.

  • Action Traits ValidateWrapper and DbActionWrapper both implement the Action trait, so they can each be treated the same way. This is important, because the perform! macros only work on types that implement Action. Action is defined twice. Once in aardwolf-rocket/src/action.rs and once in aardwolf-actix/src/action.rs. This is because they act differently for each web framework.

  • Templates are defined in aardwolf-templates/templates, but you dont interact with them directly from code. In order to use a template, there must be a struct that implements Renderable(defined inaardwolf-templates/src/lib.rs) that is associated with that template. typically, that means the struct has a similar name ( SignUpforsign_up.rs.html), and its definition of render should call that template. Renderable only needs to be defined for structs related to top-level templates, since those are the only ones that should be rendered directly from a front-end application. Top-level template refers to a template that is rendered directly, and not a template that is embedded inside another template.

  • The application is served as a webapp via the aardwolf-rocket and aardwolf-actix crates. We have two that we are developing in parallel because it forces us to make design decisions that result in portable code. Each frontend has a unique paradigm that makes writing generic code very interesting.

    • There is minimal developer overhead in the rocket frontend, since database operations with diesel are synchronous, and therefore easy to plug into a synchronous web framework.

    • There is some developer overhead in the actix-web frontend, since actix-web expects things to happen asynchronously. The merging of synchronous and asynchronous styles in the actix-web frontend is facilitated by the DbActor, which handles synchronous operations, but has an asynchronous API. We send types that implment DbAction to the DbActor, and it sends back the result of the action. Similar interfaces can be built for other synchronous tasks, if we need to do so.

Clone this wiki locally