Skip to main content

Anatomy of an Application

Applications and Services

An Oryonix application consists of one or more services. Each service has its own codebase and Git repository, and may be independently updated. However, deployments are done at the application level, meaning that all services within an application are deployed together into a particular environment (e.g. staging or production).

Different applications within the same workspace may be placed in different GitHub organizations, but all services for an application must live within that application's GitHub organization. Each service has a GitHub repository named <application_name>-<service_name>, which contains the code for that service.

Each service's GitHub repository must be set up to build and upload artifacts for that service to Oryonix whenever changes are pushed to the repository. (This is done for you automatically if you create the repository using Oryonix.) Before an application is ready to be deployed, artifacts for each of the application's services must have been uploaded.

The rest of this guide explains how to structure your services for maintainability and scalability.

Service Directory Structure

If you use Oryonix to create your service's Git repository, a skeleton that follows these conventions is automatically generated.

<repository>/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── src/
│ └── main.py
├── spec/
│ └── service.yaml
└── README.md

Files You Create

.github/workflows/ci.yml

This file contains instructions for GitHub Actions. Whenever you push changes to your service's Git repository, GitHub Actions will automatically build your service and upload it to Oryonix for deployment. You can customize this file to add additional steps to your CI pipeline, such as running tests or linters. However, the build and upload steps must be present for your service to be deployable.

Learn more about how to write workflows for GitHub Actions here:

.gitignore

This file tells Git which files or directories to ignore when committing changes. By default, it may include build artifacts generated by Oryonix, such as compiled WASM binaries. You can customize this file to ignore any files that you don't want to be tracked in your Git repository.

Learn more about writing .gitignore files here:

src/

The root of your Python module. This is where all your business logic and code lives.

The main entry point for your service must be in src/main.py. This is the file that GitHub Actions will build and upload to Oryonix. You can create additional Python files and subdirectories within src/ to organize your code as needed.

spec/

Contains OpenAPI specifications that define your API endpoints and map them to functions. The service spec file (service.yaml) is required and must be in this directory. You can also create additional spec files for other API surfaces if needed.

Auto-Generated Files

artifacts/

Contains compiled WASM binaries and other build artifacts. This directory is auto-generated by the build process and should not be manually modified. You should ignore this directory in your Git repository by adding it to your .gitignore file.

Organizing Your Code

For small services with limited functionality, a single main.py file may be all that is needed.

However, as your service grows in complexity, you may wish to split functionality out into different modules for better organization and maintainability. Oryonix supports this by allowing you to create additional Python files and subdirectories within src/ to organize your code into packages and modules, just as you would organize any other Python program.

🚧coming soon

There is one limitation, however, which is that all flow and action functions must be defined in main.py. They cannot be imported from other modules. We hope to remove this limitation soon.

To work around this, you can define thin wrappers in main.py that call functions defined in other modules. This way, you can still organize your code into multiple files and modules, while keeping all flow and action function definitions in main.py. For example:

# src/main.py

import onix
from . import payment

@onix.flow
def process_payment(invoice_id: str, payment_id: str, amount: float):
return payment.process(invoice_id, payment_id, amount)
# src/payment.py

def process(invoice_id: str, payment_id: str, amount: float):
# Payment processing logic here
return "Payment processed"