diff --git a/src/commands/dumpDb.ts b/src/commands/dumpDb.ts
index e6b4afd..5f67f7c 100644
--- a/src/commands/dumpDb.ts
+++ b/src/commands/dumpDb.ts
@@ -1,5 +1,6 @@
 import { DumpDbOptions, Registry } from "../types";
-import { DBService, getLogger } from "../utils";
+import { DBService } from "../services";
+import { getLogger } from "../utils";
 
 /**
  * Dump the database as JSON to STDOUT for a given chain ID
diff --git a/src/commands/run.ts b/src/commands/run.ts
index b3fad34..0ad7745 100644
--- a/src/commands/run.ts
+++ b/src/commands/run.ts
@@ -1,6 +1,6 @@
 import { RunOptions } from "../types";
-import { getLogger, DBService, ApiService } from "../utils";
-import { ChainContext } from "../domain";
+import { getLogger } from "../utils";
+import { DBService, ApiService, ChainContext } from "../services";
 
 /**
  * Run the watch-tower 👀🐮
diff --git a/src/domain/addContract.ts b/src/domain/events/index.ts
similarity index 98%
rename from src/domain/addContract.ts
rename to src/domain/events/index.ts
index a2d8773..c6c8c33 100644
--- a/src/domain/addContract.ts
+++ b/src/domain/events/index.ts
@@ -4,7 +4,7 @@ import {
   handleExecutionError,
   isComposableCowCompatible,
   metrics,
-} from "../utils";
+} from "../../utils";
 import { BytesLike, ethers } from "ethers";
 
 import {
@@ -17,9 +17,9 @@ import {
   Owner,
   Proof,
   Registry,
-} from "../types";
+} from "../../types";
 
-import { ChainContext } from "./chainContext";
+import { ChainContext } from "../../services/chain";
 import { ConditionalOrderParams } from "@cowprotocol/cow-sdk";
 
 /**
diff --git a/src/domain/index.ts b/src/domain/index.ts
index 340b139..21b0ef9 100644
--- a/src/domain/index.ts
+++ b/src/domain/index.ts
@@ -1 +1,2 @@
-export * from "./chainContext";
+export * as events from "./events";
+export * as polling from "./polling";
diff --git a/src/utils/filterOrder.ts b/src/domain/polling/filtering/badOrder.ts
similarity index 79%
rename from src/utils/filterOrder.ts
rename to src/domain/polling/filtering/badOrder.ts
index 1053b0e..6ccbb0c 100644
--- a/src/utils/filterOrder.ts
+++ b/src/domain/polling/filtering/badOrder.ts
@@ -1,12 +1,14 @@
 import { Order } from "@cowprotocol/contracts";
 import { BigNumber, ethers } from "ethers";
 
+const MINIMUM_VALIDITY_SECONDS = 60;
+
 /**
  * Process an order to determine if it is valid
  * @param order The GPv2.Order data struct to validate
  * @throws Error if the order is invalid
  */
-export function validateOrder(order: Order) {
+export function check(order: Order) {
   // amounts must be non-zero
   if (BigNumber.from(order.sellAmount).isZero()) {
     throw new Error("Order has zero sell amount");
@@ -30,8 +32,11 @@ export function validateOrder(order: Order) {
     throw new Error("Order has identical sell and buy token addresses");
   }
 
-  // Check to make sure that the order has at least 120s of validity
-  if (Math.floor(Date.now() / 1000) + 60 > Number(order.validTo)) {
+  // Check to make sure that the order has at least a specified validity
+  if (
+    Math.floor(Date.now() / 1000) + MINIMUM_VALIDITY_SECONDS >
+    Number(order.validTo)
+  ) {
     throw new Error("Order expires too soon");
   }
 }
diff --git a/src/domain/polling/filtering/index.ts b/src/domain/polling/filtering/index.ts
new file mode 100644
index 0000000..6ed09dc
--- /dev/null
+++ b/src/domain/polling/filtering/index.ts
@@ -0,0 +1,2 @@
+export * as badOrder from "./badOrder";
+export * as policy from "./policy";
diff --git a/src/utils/filterPolicy.ts b/src/domain/polling/filtering/policy.ts
similarity index 95%
rename from src/utils/filterPolicy.ts
rename to src/domain/polling/filtering/policy.ts
index b4be38a..d0fe2b7 100644
--- a/src/utils/filterPolicy.ts
+++ b/src/domain/polling/filtering/policy.ts
@@ -1,5 +1,5 @@
 import { ConditionalOrderParams } from "@cowprotocol/cow-sdk";
-import { Config, FilterAction as FilterActionSchema } from "../types";
+import { Config, FilterAction as FilterActionSchema } from "../../../types";
 
 export enum FilterAction {
   DROP = "DROP",
diff --git a/src/domain/checkForAndPlaceOrder.ts b/src/domain/polling/index.ts
similarity index 98%
rename from src/domain/checkForAndPlaceOrder.ts
rename to src/domain/polling/index.ts
index fcdb32b..7eccd30 100644
--- a/src/domain/checkForAndPlaceOrder.ts
+++ b/src/domain/polling/index.ts
@@ -8,14 +8,13 @@ import {
 import { ethers } from "ethers";
 import { BytesLike } from "ethers/lib/utils";
 
-import { ConditionalOrder, OrderStatus } from "../types";
+import { ConditionalOrder, OrderStatus } from "../../types";
 import {
   formatStatus,
   getLogger,
-  pollConditionalOrder,
   handleOnChainCustomError,
   metrics,
-} from "../utils";
+} from "../../utils";
 import {
   ConditionalOrder as ConditionalOrderSDK,
   OrderBookApi,
@@ -30,9 +29,9 @@ import {
   SupportedChainId,
   formatEpoch,
 } from "@cowprotocol/cow-sdk";
-import { ChainContext, SDK_BACKOFF_NUM_OF_ATTEMPTS } from "./chainContext";
-import { FilterAction } from "../utils/filterPolicy";
-import { validateOrder } from "../utils/filterOrder";
+import { ChainContext, SDK_BACKOFF_NUM_OF_ATTEMPTS } from "../../services";
+import { badOrder, policy } from "./filtering";
+import { pollConditionalOrder } from "./poll";
 
 const GPV2SETTLEMENT = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41";
 
@@ -125,12 +124,12 @@ export async function checkForAndPlaceOrder(
         });
 
         switch (filterResult) {
-          case FilterAction.DROP:
+          case policy.FilterAction.DROP:
             log.debug("Dropping conditional order. Reason: AcceptPolicy: DROP");
             ordersPendingDelete.push(conditionalOrder);
 
             continue;
-          case FilterAction.SKIP:
+          case policy.FilterAction.SKIP:
             log.debug("Skipping conditional order. Reason: AcceptPolicy: SKIP");
             continue;
         }
@@ -337,7 +336,7 @@ async function _processConditionalOrder(
 
     // We now have the order, so we can validate it. This will throw if the order is invalid
     // and we will catch it below.
-    validateOrder(orderToSubmit);
+    badOrder.check(orderToSubmit);
 
     // calculate the orderUid
     const orderUid = _getOrderUid(chainId, orderToSubmit, owner);
diff --git a/src/utils/poll.ts b/src/domain/polling/poll.ts
similarity index 95%
rename from src/utils/poll.ts
rename to src/domain/polling/poll.ts
index b3be63e..36b0384 100644
--- a/src/utils/poll.ts
+++ b/src/domain/polling/poll.ts
@@ -5,7 +5,7 @@ import {
   PollParams,
   PollResult,
 } from "@cowprotocol/cow-sdk";
-import { getLogger } from "./logging";
+import { getLogger } from "../../utils/logging";
 
 // Watch-tower will index every block, so we will by default the processing block and not the latest.
 const POLL_FROM_LATEST_BLOCK = false;
diff --git a/src/utils/api.ts b/src/services/api.ts
similarity index 96%
rename from src/utils/api.ts
rename to src/services/api.ts
index e6fd1b8..1d5b47e 100644
--- a/src/utils/api.ts
+++ b/src/services/api.ts
@@ -2,11 +2,10 @@ import { Server } from "http";
 import express, { Request, Response, Router } from "express";
 import { Express } from "express-serve-static-core";
 import * as client from "prom-client";
-import { getLogger } from "./logging";
-import { DBService } from "./db";
+import { getLogger } from "../utils/logging";
+import { DBService, ChainContext } from "../services";
 import { Registry } from "../types";
 import { version, name, description } from "../../package.json";
-import { ChainContext } from "../domain";
 
 export class ApiService {
   protected port: number;
diff --git a/src/domain/chainContext.ts b/src/services/chain.ts
similarity index 98%
rename from src/domain/chainContext.ts
rename to src/services/chain.ts
index c4cdcf7..0aa504f 100644
--- a/src/domain/chainContext.ts
+++ b/src/services/chain.ts
@@ -14,18 +14,18 @@ import {
   OrderBookApi,
   ApiBaseUrls,
 } from "@cowprotocol/cow-sdk";
-import { addContract } from "./addContract";
-import { checkForAndPlaceOrder } from "./checkForAndPlaceOrder";
+import { addContract } from "../domain/events";
+import { checkForAndPlaceOrder } from "../domain/polling";
 import { EventFilter, providers } from "ethers";
 import {
   composableCowContract,
-  DBService,
   getLogger,
   isRunningInKubernetesPod,
   metrics,
 } from "../utils";
+import { DBService } from ".";
 import { hexZeroPad } from "ethers/lib/utils";
-import { FilterPolicy } from "../utils/filterPolicy";
+import { policy } from "../domain/polling/filtering";
 
 const WATCHDOG_FREQUENCY_SECS = 5; // 5 seconds
 const WATCHDOG_TIMEOUT_DEFAULT_SECS = 30;
@@ -86,7 +86,7 @@ export class ChainContext {
   chainId: SupportedChainId;
   registry: Registry;
   orderBook: OrderBookApi;
-  filterPolicy: FilterPolicy | undefined;
+  filterPolicy: policy.FilterPolicy | undefined;
   contract: ComposableCoW;
   multicall: Multicall3;
 
@@ -129,7 +129,7 @@ export class ChainContext {
       },
     });
 
-    this.filterPolicy = new FilterPolicy(filterPolicy);
+    this.filterPolicy = new policy.FilterPolicy(filterPolicy);
     this.contract = composableCowContract(this.provider, this.chainId);
     this.multicall = Multicall3__factory.connect(MULTICALL3, this.provider);
   }
diff --git a/src/services/index.ts b/src/services/index.ts
new file mode 100644
index 0000000..413687c
--- /dev/null
+++ b/src/services/index.ts
@@ -0,0 +1,7 @@
+import { DBService } from "./storage";
+import { ApiService } from "./api";
+import { ChainContext, SDK_BACKOFF_NUM_OF_ATTEMPTS } from "./chain";
+
+// Exporting the `SDK_BACKOFF_NUM_OF_ATTEMPTS` is a smell that can be eliminated
+// when upstream (`cow-sdk`) allows passing instantiated `OrderBookApi` for `.poll`.
+export { DBService, ApiService, ChainContext, SDK_BACKOFF_NUM_OF_ATTEMPTS };
diff --git a/src/utils/db.ts b/src/services/storage.ts
similarity index 95%
rename from src/utils/db.ts
rename to src/services/storage.ts
index f5d0288..b0744fa 100644
--- a/src/utils/db.ts
+++ b/src/services/storage.ts
@@ -1,5 +1,5 @@
 import { DatabaseOptions, Level } from "level";
-import { getLogger } from "./logging";
+import { getLogger } from "../utils/logging";
 
 const DEFAULT_DB_LOCATION = "./database";
 
diff --git a/src/types/model.ts b/src/types/model.ts
index cc7b94b..c31228e 100644
--- a/src/types/model.ts
+++ b/src/types/model.ts
@@ -4,7 +4,8 @@ import { BytesLike, ethers } from "ethers";
 
 import type { ConditionalOrderCreatedEvent } from "./generated/ComposableCoW";
 import { ConditionalOrderParams, PollResult } from "@cowprotocol/cow-sdk";
-import { DBService, metrics } from "../utils";
+import { DBService } from "../services";
+import { metrics } from "../utils";
 
 // Standardise the storage key
 const LAST_NOTIFIED_ERROR_STORAGE_KEY = "LAST_NOTIFIED_ERROR";
diff --git a/src/utils/context.ts b/src/utils/context.ts
index b48efe6..a094229 100644
--- a/src/utils/context.ts
+++ b/src/utils/context.ts
@@ -1,5 +1,5 @@
 import Slack = require("node-slack");
-import { DBService } from "./db";
+import { DBService } from "../services";
 
 import { ContextOptions, ExecutionContext, Registry } from "../types";
 import { SupportedChainId } from "@cowprotocol/cow-sdk";
diff --git a/src/utils/index.ts b/src/utils/index.ts
index c5fbd7c..b263022 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,8 +1,5 @@
 export * from "./context";
 export * from "./contracts";
-export * from "./poll";
 export * from "./misc";
-export * from "./db";
 export * from "./logging";
-export * from "./api";
 export * as metrics from "./metrics";