Homepage platformOS Logo

Core Concepts of the pOS Product Marketplace Template

Last edit: Dec 16, 2020

The pOS Product Marketplace Template is a fully functional marketplace built on platformOS with features like user onboarding, ad listings and ads, purchase and checkout process, and online payment. Following the tutorial, you can deploy this code within minutes to have a list of working features and start customizing the back- and front-end code.

This topic describes the core concepts like the business logic, presentation logic, data queries, categories, and tests for the pOS Product Marketplace Template.


To explore the pOS Product Marketplace Template, follow our Get Started guide to set up your development environment and install the template.

Business logic

General rules

Business logic and presentation logic are separated and should not interfere with each other, meaning:

  • no HTML tags in business logic
  • no data queries in presentation layer


Command is our concept to encapsulate business rules. By following our recommendation, you can improve the consistency of your code to make it easier to onboard new developers to the project and take over existing projects. We use the same pattern for all of our templates. The advantage of using this architecture is that it is easy to re-use the command - you can execute it in a live web request as well as a background job. It is also easy to copy it across different projects.

Command are located in /app/views/partials/lib/commands

  • For business logic use commands

  • A generic command consists of 3 stages:

    • Build: This is the place where you build input for the command; if you are proficient with platformOS - equivalent of Form's default_payload
    • Validate: This is the place where you validate the input - for example, you ensure all required fields are provided, you check uniqueness, check the format of the input (numbers are really numbers and not letters), etc. This always returns a hash with two keys - valid being either true or false, and if false - errors with details why validation has failed.
    • Execute: If validation succeeds, proceed with executing the command. Any error raised here should be considered a 500 server error. If you allow errors here, it means there is something wrong with the code organisation, as all checks to prevent errors should be done in the validate step.
  • Commands are designed to be easily executed as background jobs [heavy commands - external API call, expensive operations computations, reports]

  • Each command might produce an event

Command workflow

This command workflow describes the three stages and validations of a command.

Process diagram of the command workflow - build, check, execute


  • Security first
  • Build object based on preferred command schema
  • Collect data from various sources: session, user input, database
  • Cast types and convert, for example, string into arrays, string to integer, etc.
  • Filter out forbidden parameters from user input

Example: context.exports.object

  "name": "ObjectName",
  "price": null


  • Validate the object properties data types
  • Validate the object against business rules
  • User permission check: Can the user edit a specific field?


  "name": "ObjectName",
  "price": null,
  "errors": {
    "price": ["app.errors.blank"]
  "valid": false

If the object validation fails:

  • the user should correct data soft errors
  • render form with error messages

If the object validation succeeds, proceed to the Execute stage.


Execute command core function, for example:

  • Save the object in the database
  • Send API request
  • Make payment

If the object validation fails:

  • Log error
  • Notify development team
  • Render 500 error

If the object validation succeeds, redirect and notify the user about the successful operation.

Data queries

Data queries are located in app/views/partials/lib/data/queries

  • Generally, these are wrappers on GraphQL queries

Presentation views: HTML/JSON

To ensure a maintainable and easy to change frontend, we follow a couple of important rules.

First of all, all our frontend code is inside the theme directory. Those files should not know about the existence of any other files outside of theme. All data needed for the frontend should be explicitly provided to them - there shouldn't be any GraphQL queries inside theme. If you need extra data that are not provided by default, we suggest to make all GraphQL queries inside a page (which you can treat as a Controller in the MVC architecture) and explicitly provide the result of this query to the partial.

Theme is located in app/views/partials/theme

  • Partials to be aware of ONLY local variables - no context.session OR context.exports are allowed, think Dependency Injection
  • Prepare/fetch external data in a page and pass it to partials as a local variable
  • No GraphQL queries are allowed within the theme folder. Theme is used to display data, not fetch it


  • Each command produces an event
  • Example: when a user logs in the system produces the user_session_created event, which looks similar to this: { actor: { id: LOGGED_USER_ID } }
  • Then the event can be asynchronously consumed by a consumer


Consumers are located in app/views/partials/lib/consumers.


Categories can be adjusted by:

  1. First editing app/views/partials/lib/queries/raw_category_list.liquid file
  2. Then adding a translation to the app/translations/en_categies.liquid file


e2e tests

Testcafe tests are located in the test/ directory. To run them on your local machine, run:

npm test

Contribute to this page

Github Icon


We are always happy to help with any questions you may have.

contact us