Skip to content

How the services fit together

We run two generations of platform at once. Almost every confusing thing about our architecture follows from that, so it is the first thing worth understanding.

The legacy NettiX estate serves the marketplaces today: PHP and Symfony, Aurora MySQL, Elasticsearch. Mobility One is what replaces it: Next.js, PostgreSQL, OpenSearch. Neither is a rewrite of the other in place — they run side by side, and a set of shared services sits under both.

LEGACY NETTIX MOBILITY ONE ADJACENT: PRO / DEALER
───────────── ──────────── ──────────────────────
who consumers consumers dealers
│ │ │
▼ ▼ ▼
web nettix-web, nettix-www m1-web apps aso-* (NettiXPro)
Symfony / PHP Next.js on ECS Fargate nettix-webstore (TEHO)
│ │ via m1-api-client
▼ ▼
API nettix-api ◀── apps-api ──▶ APIv2
│ ▲ │
▼ │ ▼
data Aurora MySQL │ PostgreSQL ──sync──▶ OpenSearch
Elasticsearch │ (m1-atlas)
mobile apps (iOS, Android)
shared by both generations: image service, ajomark-header, counter-service,
event bus, Salesforce

Read it as a grid. Each column is one generation, and each row is one layer: who uses it, the web tier, the API tier, the data tier. The web, API and data rows summarise the tables below, which add the layers the picture leaves out. The right-hand column is the professional and dealer side, which belongs to neither generation. apps-api sits between the two API tiers because it is the backend for the mobile apps and reads from both.

Layer Repositories
Web nettix-web (responsive), nettix-www (classic monolith)
API nettix-api (api.nettix.fi), apps-api (mobile), admin-api
Auth auth-api
Data Aurora MySQL (via RDS); Elasticsearch for search on Nettivene, Nettikone and Nettivaraosa only; caching in Redis, APCu and Memcache

The verticals are split across the two web codebases rather than migrated cleanly, and the split moves as verticals are ported. Check which codebase a vertical is on before assuming: the glossary has the current mapping.

Layer Repositories
Web m1-web — one Turborepo monorepo holding per-vertical and multi-tenant Next.js apps on shared UI, deployed to ECS Fargate
API client m1-api-client — the typed npm client for APIv2
Data m1-atlas — PostgreSQL as the system of record, schema and migrations as code
Search OpenSearch as the read side, synced from Postgres

Two decisions explain most of its shape, and both are recorded rather than folklore: ECS Fargate over Lambda (m1-web ADR-0001), and Postgres as the system of record with OpenSearch as a read side (m1-atlas ADR-0013). The decision index lists the rest.

One data-model consequence is worth knowing before you read any M1 code: a vehicle and a listing are separate, the vertical lives on the listing, and one vehicle can carry several listings (m1-atlas ADR-0011). Multi-tenancy is a property of the data, not of a per-vertical deployment.

These are the services that make “which generation is this” the wrong question.

Service Does
alma-mobility-image-service Serves and transforms every listing image, routed per vertical by an x-vertical-id origin header behind CloudFront
ajomark-header The global site header, as a library both generations consume — so navigation stays identical while the pages underneath differ
counter-service View and event counting — see Analytics and experimentation
ajo-event-relay + ajo-event-rules Get events out of the legacy AWS estate and onto the central Alma event bus, and route them there
alma-ajo-salesforce Salesforce as the customer-relationship (CRM) and dealer system of record

The header is the clearest example of the pattern we reach for: rather than duplicating a component per vertical per generation, one library is published and both consume it (m1-web ADR-0012).

Neither generation owns these; both sit on them.

Concern What we use
Compute Elastic Container Service (ECS) with Docker, plus EC2 for the older estate
Messaging The AWS messaging services — SNS, SQS and EventBridge, which carries the central event bus
Delivery CloudFront and Cloudflare in front of the sites
Search Elasticsearch on the legacy side, OpenSearch on the Mobility One side
Infrastructure as code Terraform — infra for the shared estate, infra-v2 for the split-account estate, infra-cloudflare for Cloudflare, and embedded Terraform in individual service repositories

Measurement is its own small estate, and it is the part people most often do not know exists until a number looks wrong.

Piece What it does
counter-service View and event counting. The Frontend Counter Service (FECS) runs at the edge on Cloudflare Zaraz, through API Gateway into DynamoDB; the Backend Counter Service (BECS) is DynamoDB behind Lambda
Google Tag Manager Analytics event tracking on the sites
AB Tasty A/B testing and experimentation

Products with their own stacks, which the two-generation split does not describe well. Some carry their own Jira project and some do not — the Jira projects table is the list that owns that.

  • Autotalli — a Java stack, its own Terraform, its own project (AT).
  • Baana — the consumer-to-business auction site, TypeScript front and back (AJOC2B).
  • OmaAjo (my-vehicles) — consumer vehicle management, React and PHP.
  • NettiXPro and TEHO — the professional and dealer side, at app.nettixpro.fi (the aso-* repositories) and teho.nettix.fi (nettix-webstore).

Each of these is a service the marketplaces call, not a platform layer: ajo-kisu-api (recommendations from the KISU model), spede (user trust scoring), neural-engine-for-registration-obfuscation (plate blurring), and Hakuapuri (semantic search, in the almamedia organisation).

Six external systems own data we do not:

  • Alma Tunnus — end-user SSO, provided centrally by Alma. Unrelated to the AWS SSO engineers use.
  • Trafi — the Finnish vehicle-data API. Vehicle facts originate there.
  • MAJO — Alma Media’s internal vehicle-data service. A second origin for vehicle facts alongside Trafi, so “where did this car’s data come from” has two answers.
  • Salesforce — the customer-relationship (CRM) records.
  • WebSales Cloud — the dealer management system.
  • Sourcepoint — the consent management platform (CMP) behind the cookie and tracking consent the sites collect.