Create your own
Lesson illustration

Defining a Domain Vocabulary for Process Instrumentation and Tracing

Hello, and welcome to the DDD-informed architecture module. The tool you are building is not merely “a Frida wrapper”: its durable value is helping an operator understand an authorized target process through a coherent sequence of discovery, instrumentation, observation, and recording. This lesson establishes the language that makes that model coherent before we divide it into feature slices or connect it to Frida and the terminal.

By the end, you will have a bounded vocabulary for processes, instrumentation sessions, probes, trace events, and recordings, plus rules for keeping Windows, Frida, and CLI terminology from leaking into the core model.


Start with a boundary, not a class diagram

Domain-Driven Design uses a bounded context to make the meaning of a model explicit. It is a boundary inside which terms have one agreed meaning. The boundary is not automatically a folder, package, service, or repository. Those may later help enforce it, but the essential boundary is semantic: it tells the team when a word is valid and what it means.

Domain-‐Driven Design Reference

Read Eric Evans's concise definitions of domain, model, ubiquitous language, and bounded context. They provide the criteria for deciding what belongs in the vocabulary of this tool.

On the printed page numbered vii, read the definition set. Then read Section 2, “Bounded Context,” through the paragraph beginning “Explicitly define the context within which a model applies.” Focus on the idea that a model is only meaningful within its stated context, rather than attempting to represent every technical concern in one universal model.

For this application, define one initial bounded context:

Local Instrumentation is the context in which an operator discovers an authorized local Windows process, selects it as a target, creates an instrumentation session, manages probes, observes trace events, and optionally creates a recording.

This definition is intentionally operational. The tool exists to help an operator inspect a running application safely and intelligibly. It is not a general model of all Windows process internals, all reverse-engineering techniques, or Frida’s entire API.

A good bounded context lets you say precise things such as:

  • “This instrumentation session targets this process.”
  • “This probe observed this trace event.”
  • “This recording contains events from one session.”
  • “The Frida script was destroyed” is a technical integration fact, not automatically a domain event.
  • “The user entered a command string” belongs to terminal input, not to the instrumentation domain.

The last two distinctions will prevent a common architecture failure: allowing the vocabulary of a library, transport, or UI to become the application’s vocabulary.

Want to Start with DDD? Try Ubiquitous Language First!

Watch “Want to Start with DDD? Try Ubiquitous Language First!” from Zoran on C#. It demonstrates why terms should be captured and clarified before jumping to entities, aggregates, or implementation details.

Watch vocabulary first for the case for establishing a shared language before designing tactical DDD structures. Continue with refining terms, paying attention to the practice of recording terms and replacing vague initial names with the words domain experts actually mean. Here, the relevant experts include the intended tool users and the developers who understand Windows and Frida behavior.


One word can legitimately mean different things

Bounded contexts are useful because identical words often carry different meanings in different parts of a system. Martin Fowler’s bounded-context sketch makes that visible: Customer and Product occur in both Sales and Support, but their surrounding relationships and relevant attributes differ.

The diagram shows “Customer” and “Product” in both Sales and Support contexts, where each occurrence has different relationships and therefore potentially different meanings. The same separation protects the instrumentation model from Frida, Windows, terminal, and storage terminology.

For this project, the most dangerous overloaded term is session:

ContextMeaning of “session”
Frida integrationA frida.Session object representing an attachment managed by Frida
Interactive terminalThe lifetime of one REPL invocation
Local InstrumentationA user-meaningful InstrumentationSession that tracks work performed against a selected target process
Recording storageA session identifier written with captured events

These meanings overlap, but they are not interchangeable. In domain code, prefer InstrumentationSession. In the Frida adapter, use the library’s Session type and translate it at the boundary. This avoids imports such as frida.Session spreading into command handlers, trace models, or recording logic.

The same discipline applies to event. A console warning, a Node.js EventEmitter notification, a raw Frida send() payload, and an observed native call are all “events” in ordinary speech. Only one of them is a TraceEvent in the Local Instrumentation context.

The context boundary

The following table provides a practical ownership rule.

ConcernLocal Instrumentation languageExternal or technical language
Windows executionProcessDescriptor, TargetProcess, ProcessId, optional ThreadIdWin32 structures, handles, process access rights, OS scheduler details
Frida integrationInstrumentationSession, Probe, TraceEventDevice, Session, Script, Interceptor, raw message payload
Terminal interactionapplication command names such as attach or list-processestokenization, ANSI styling, readline history, tab completion
PersistenceRecording, RecordingId, recorded trace dataNDJSON lines, paths, file handles, write buffers
Fault reportingdomain-oriented diagnostic such as ProbeResolutionFailedJavaScript exception, Frida error object, filesystem error

This does not mean external terms are forbidden. They belong where they are accurate: adapters and infrastructure. The rule is that their types and accidental semantics should not define the core model.

For example, a Frida Script may be necessary to implement an instrumentation session, but it is not the session itself from the operator’s perspective. Likewise, an NDJSON file is one representation of a recording, not the meaning of a recording.


The initial ubiquitous language

The glossary below is deliberately compact. It defines the concepts needed for the first usable version of the tool without prematurely designing every field or lifecycle rule.

TermMeaning within Local InstrumentationImportant distinction
ProcessAn operating-system execution environment that can be discovered and, if authorized, selected for inspection. It has a runtime identity and may expose descriptive metadata such as executable name.A process is not a Frida device, a terminal command, or a static executable file.
ProcessIdThe Windows runtime identifier used to locate a process during its lifetime.A PID is a locator, not a permanent business identity. Windows may reuse it after a process exits.
ProcessDescriptorAn immutable discovery result describing a process at the time the tool observed it.It is a snapshot, not a live process controller.
TargetProcessThe process currently selected for instrumentation by an operator.“Target” expresses the role a process plays in a session; it is not a different kind of Windows process.
InstrumentationSessionA user-meaningful unit of instrumentation work associated with one target process. It groups attachment, active probes, observations, and optional recording.It is not the same thing as a Frida Session instance or the lifetime of the REPL.
ProbeA requested observation point managed within an instrumentation session, such as an exported function or declared Windows API.A hook or interceptor is an implementation mechanism that may realize a probe.
Probe locationThe logical place the probe intends to observe, expressed without a raw runtime address where possible.A runtime address is resolved later and is process-specific.
TraceEventAn immutable record that the tool has observed something relevant in the target process, such as an intercepted call.It is not a raw agent message, log line, exception, or UI notification.
Trace payloadThe event-specific observed data, such as decoded arguments, return information, or contextual metadata.It must not expose arbitrary Frida objects or unsafe raw memory reads to the rest of the application.
RecordingA durable, ordered capture of session-related trace data intended for later examination.It is not synonymous with the file format or file path used to store it.
Recording entryOne persisted unit within a recording, eventually containing event data plus recording metadata.The exact envelope and versioning rules come later.
Diagnostic errorA structured explanation of why an intended instrumentation action could not be completed safely or correctly.It is not simply the text of a caught exception.

Two supporting terms are worth including now:

  • A thread is an execution path within a process. The processor runs threads, while a process provides the virtual memory, code, data, and system resources those threads use. Trace events may later include a ThreadId as execution context, but a thread is not yet a core aggregate of this tool.
  • An agent is code loaded into a target process to perform local observation. It is an implementation participant, not the owner of the domain model. The agent produces data that the host must interpret and validate.

Notice the deliberate use of nouns that an operator can understand. A phrase such as “add a probe to the session” says what the system is doing. By contrast, “register an interceptor callback” says how one particular adapter may do it.


Choose entity, value, and event language carefully

Once the vocabulary is coherent, DDD’s tactical categories become useful. They should sharpen the model, not be applied mechanically.

Domain-‐Driven Design Reference

Return to the “Building Blocks of a Model-Driven Design” section. These excerpts give a useful test for deciding which concepts require continuity of identity, which are immutable descriptions, and which represent facts that occurred.

In Section II, “Building Blocks of a Model-Driven Design,” read the printed pages 11 through 13. First read the entity guidance. Then read the value-object guidance, followed by the event guidance. Relate each category to the proposed glossary rather than treating the categories as a class-design recipe.

Here is a useful initial classification:

ConceptLikely classificationWhy
InstrumentationSessionEntityIt persists through a lifecycle and remains the same session as probes are added or recording begins.
ProbeEntity, owned by a sessionIt needs stable identity so the operator can list or remove the intended observation point even as its implementation state changes.
RecordingEntity or durable artifact with identityIt is a named result of a particular capture activity, even if its content grows over time.
ProcessDescriptorValue objectIt describes what discovery observed at a particular moment.
ProcessId, SessionId, ProbeId, RecordingIdValue objectsTheir meaning comes from the value and its type, not an independent lifecycle.
TraceEventImmutable eventIt records an observation that already occurred and should not be mutated after capture.
Probe location and filter optionsValue objectsThey describe intent and are naturally replaced rather than modified in place.

There is a subtle but important distinction between a TraceEvent and a future domain event about session activity.

  • A trace event says something happened in the target process: for example, an observed call to a Windows API.
  • A session-action event says something happened in the tool’s domain: for example, a probe was added or a recording was started.

Both are immutable facts. They have different sources, audiences, and payloads. Keeping them separate now avoids turning every internal lifecycle change into a fake target-process observation.


Express the language in TypeScript without binding it to Frida

A glossary becomes useful only when it affects code. The following is an intentionally incomplete domain-facing sketch. It communicates naming and ownership; it is not yet a complete persistence schema, Frida protocol, or state machine.

// domain/local-instrumentation/vocabulary.ts

export type ProcessId = number & {
  readonly brand: "ProcessId";
};

export type InstrumentationSessionId = string & {
  readonly brand: "InstrumentationSessionId";
};

export type ProbeId = string & {
  readonly brand: "ProbeId";
};

export type RecordingId = string & {
  readonly brand: "RecordingId";
};

export interface ProcessDescriptor {
  readonly processId: ProcessId;
  readonly imageName: string;
}

export interface TargetProcess {
  readonly processId: ProcessId;
}

export interface InstrumentationSession {
  readonly sessionId: InstrumentationSessionId;
  readonly targetProcess: TargetProcess;
}

export interface Probe {
  readonly probeId: ProbeId;
  readonly sessionId: InstrumentationSessionId;
  readonly location: ProbeLocation;
}

export interface TraceEvent {
  readonly sessionId: InstrumentationSessionId;
  readonly probeId: ProbeId;
  readonly observedAt: Date;
  readonly payload: TracePayload;
}

export interface Recording {
  readonly recordingId: RecordingId;
  readonly sessionId: InstrumentationSessionId;
}

export interface ProbeLocation {
  readonly description: string;
}

export interface TracePayload {
  readonly kind: string;
}

Several design choices matter more than the placeholder fields:

  1. The domain names are explicit. InstrumentationSession is more precise than Session; TargetProcess describes role rather than operating-system type.

  2. The domain has no import "frida". A Frida adapter can translate frida.Process data into ProcessDescriptor, and an agent transport adapter can translate a decoded message into TraceEvent. The domain does not need to know how either is implemented.

  3. Trace event payloads remain a domain abstraction. A raw pointer, a Frida NativePointer, or an arbitrary JavaScript object must not silently cross the boundary as a payload. Later lessons will specify typed event variants and validate runtime input.

  4. The types do not imply a database design. Recording does not expose a path or a writable stream. Probe does not contain a Frida interceptor handle. Those are adapter responsibilities.

  5. No lifecycle state appears yet. A session clearly has a lifecycle, but naming every valid state and transition is the next modeling step. Avoid adding boolean fields such as isAttached and isRecording now merely because the implementation needs them.

A useful test is to scan domain code for terms such as Script, Device, readline, JSON.stringify, fileHandle, or NativePointer. Their presence is usually evidence that a boundary has been crossed too early.


Create the language artifact

Create a lightweight living document, such as docs/domain-language.md. Its purpose is not documentation after the fact; it is the reference used when naming handlers, types, commands, test cases, and adapter translations.

Use this compact structure for each term:

## Probe

**Definition:** A requested observation point managed by an
InstrumentationSession.

**Identity:** ProbeId.

**Created when:** An operator asks the tool to observe a declared location.

**Not the same as:** A Frida Interceptor instance or an agent callback.

**Related terms:** InstrumentationSession, ProbeLocation, TraceEvent.

**Example sentence:** “The session contains a probe for the selected API.”

For this lesson, document at least these five terms in that form:

  1. ProcessDescriptor
  2. InstrumentationSession
  3. Probe
  4. TraceEvent
  5. Recording

Then add a short Terminology decisions section with these decisions:

  • Use process for the OS execution environment and target process for the selected role.
  • Use instrumentation session for the domain concept and reserve bare session for a qualified external API reference.
  • Use probe for the requested observation and hook only for an implementation technique.
  • Use trace event only for an accepted observation from the target process.
  • Use recording for the logical durable capture, and qualify storage terms as recording file or NDJSON representation.

Finally, add three scenario sentences in the same vocabulary. For example:

“Process discovery returns process descriptors; the operator selects one as the target process.”
“An instrumentation session manages probes for its target process.”
“A probe can produce trace events, and a recording preserves accepted trace events for later replay.”

If a sentence forces awkward terminology, revise the glossary first. That friction is useful evidence that the model is unclear.


Key takeaways

A bounded context gives the application a stable semantic center: Local Instrumentation models an operator’s work of observing an authorized target process, not the incidental APIs of Frida, Node.js, Windows, or a file format.

Within that context:

  • InstrumentationSession, Probe, and Recording need continuity and are likely entities.
  • ProcessDescriptor and typed identifiers are value-oriented descriptions.
  • TraceEvent is an immutable observation of activity in the target process.
  • Raw Frida messages, Frida sessions, terminal input, and NDJSON files are external representations that must be translated at boundaries.
  • The vocabulary should appear consistently in code, tests, documentation, and operational conversation.

Next, you will turn this vocabulary into cohesive vertical feature slices, deciding how process discovery, interception, API tracing, and recording can evolve independently without fragmenting the model.

Can't find a good explanation? Sign up and we'll make it for you

Sign up