diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## 0.3.0.1 — 2026-04-08
+
+- Refactor: derive `Functor`, `Profunctor`, `Category`, `Arrow`, and `ArrowChoice` for `Service` via `Kleisli (ExceptT ServiceError IO)`, to which `Service` is isomorphic. No behavioral or API changes — all instances match the prior hand-written semantics exactly (including `Left` short-circuit on `>>>`, `first`, and `left`).
+- New transitive dependency: `transformers` (GHC boot library).
+
 ## 0.3.0.0 — 2026-04-07
 
 - Add `Category` instance for `Service` — compose services sequentially with `>>>`, errors short-circuit automatically
diff --git a/src/Tower/Service.hs b/src/Tower/Service.hs
--- a/src/Tower/Service.hs
+++ b/src/Tower/Service.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE DerivingVia #-}
+
 -- |
 -- Module      : Tower.Service
 -- Description : Core Service and Middleware abstractions
@@ -9,7 +11,8 @@
 --
 -- 'Service' forms a 'Category' (sequential composition via '>>>') and an
 -- 'Arrow', so you can use arrow combinators and proc-notation to wire
--- services together.
+-- services together. All instances are derived via
+-- @'Kleisli' ('ExceptT' 'ServiceError' 'IO')@, which 'Service' is isomorphic to.
 module Tower.Service
   ( Service(..)
   , Middleware
@@ -19,19 +22,25 @@
   , composeMiddleware
   ) where
 
-import Control.Arrow (Arrow(..), ArrowChoice(..))
-import Control.Category (Category(..))
+import Control.Arrow (Arrow, ArrowChoice, Kleisli(..))
+import Control.Category (Category, (.))
+import Control.Monad.Trans.Except (ExceptT(..))
 import Data.Profunctor (Profunctor(..))
+import GHC.IO (IO(..)) -- bring IO's newtype constructor into scope so DerivingVia can coerce through it
 import Prelude hiding (id, (.))
 import Tower.Error (ServiceError)
 
 -- | A service transforms a request into an effectful response.
 -- This is the fundamental building block — middleware wraps services.
 --
--- 'Service' is a 'Functor' in its response type and a 'Profunctor' over
--- both request and response types. Use 'fmap' to transform responses,
--- 'lmap' to transform requests, or 'dimap' to transform both — useful for
--- lifting a @Service Http.Request Http.Response@ into a
+-- 'Service' is isomorphic to @'Kleisli' ('ExceptT' 'ServiceError' 'IO')@,
+-- and all of its instances ('Functor', 'Profunctor', 'Category', 'Arrow',
+-- 'ArrowChoice') are derived from that representation. Composition via
+-- '>>>' short-circuits on the first 'Left'.
+--
+-- Use 'fmap' to transform responses, 'lmap' to transform requests, or
+-- 'dimap' to transform both — useful for lifting a
+-- @Service Http.Request Http.Response@ into a
 -- @Service MyBinding.SomeRequest MyBinding.SomeResponse@.
 --
 -- @
@@ -43,39 +52,9 @@
   { runService :: req -> IO (Either ServiceError res)
     -- ^ Execute the service with a request, returning either an error or a response.
   }
-
-instance Functor (Service req) where
-  fmap f (Service run) = Service $ \req -> fmap (fmap f) (run req)
-
-instance Profunctor Service where
-  dimap f g (Service run) = Service $ \req -> fmap (fmap g) (run (f req))
-  lmap f (Service run) = Service (run . f)
-  rmap = fmap
-
-instance Category Service where
-  id = Service (pure . Right)
-  (Service g) . (Service f) = Service $ \req -> do
-    result <- f req
-    case result of
-      Left err -> pure (Left err)
-      Right mid -> g mid
-
-instance Arrow Service where
-  arr f = Service (pure . Right . f)
-  first (Service f) = Service $ \(a, c) -> do
-    result <- f a
-    case result of
-      Left err -> pure (Left err)
-      Right b -> pure (Right (b, c))
-
-instance ArrowChoice Service where
-  left (Service f) = Service $ \eac -> case eac of
-    Left a -> do
-      result <- f a
-      case result of
-        Left err -> pure (Left err)
-        Right b -> pure (Right (Left b))
-    Right c -> pure (Right (Right c))
+  deriving (Functor) via (Kleisli (ExceptT ServiceError IO) req)
+  deriving (Profunctor, Category, Arrow, ArrowChoice)
+    via (Kleisli (ExceptT ServiceError IO))
 
 -- | Middleware wraps a service to add behavior (retry, timeout, logging, etc.)
 --
diff --git a/tower-hs.cabal b/tower-hs.cabal
--- a/tower-hs.cabal
+++ b/tower-hs.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           tower-hs
-version:        0.3.0.0
+version:        0.3.0.1
 synopsis:       Composable service middleware for Haskell, inspired by Rust's Tower
 description:    tower-hs provides a composable middleware abstraction for any service type.
                 Build middleware stacks with retry, timeout, circuit breaker, and more — all with
@@ -57,6 +57,7 @@
     , stm ==2.5.*
     , text >=2.0 && <2.2
     , time >=1.11 && <1.15
+    , transformers >=0.5 && <0.7
   default-language: Haskell2010
 
 test-suite tower-hs-test
