diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,68 @@
+# DataFlow
+
+Generate Graphviz documents from a Haskell representation.
+
+```
+cabal configure
+cabal build
+```
+
+## Usage
+
+![Legend](https://rawgit.com/owickstrom/dataflow/master/example/legend.svg)
+
+The objects supported by DataFlow is:
+
+* `TrustBoundary`
+* `InputOutput`
+* `Function`
+* `Flow`
+
+These are composed in a `Diagram` to get something printable.
+
+For more information see the [Hackage site](https://hackage.haskell.org/package/dataflow).
+
+## Example
+
+```haskell
+module Main where
+
+import DataFlow.Core
+import DataFlow.DFD
+
+main :: IO ()
+main = printDfd $
+  Diagram "My Diagram" [
+    TrustBoundary "browser" "Browser" [
+      Function "webapp" "Webapp"
+    ],
+    TrustBoundary "aws" "Amazon AWS" [
+      Function "server" "Web Server",
+      Database "logs" "Logs"
+    ],
+    InputOutput "analytics" "Google Analytics",
+
+    Flow "webapp" "server" "Request /" "",
+    Flow "server" "logs" "Log" "User IP",
+    Flow "server" "webapp" "Response" "User Profile",
+
+    Flow "webapp" "analytics" "Log" "Page Navigation"
+  ]
+```
+
+Then generate your output with dot.
+
+```bash
+runhaskell example.hs | dot -Tsvg > example.svg
+```
+
+That should generate something like the following.
+
+![Example Output](https://rawgit.com/owickstrom/dataflow/master/example/example.svg)
+
+## Building the Examples
+
+```bash
+cabal install
+make -C example
+```
diff --git a/dataflow.cabal b/dataflow.cabal
--- a/dataflow.cabal
+++ b/dataflow.cabal
@@ -1,16 +1,31 @@
 name:                dataflow
-version:             0.3.2.0
+version:             0.4.0.0
 synopsis:            Generate Graphviz documents from a Haskell representation.
 description:         Outputs .dot files that can be processed by the dot
-                     command. Currently it only supports the DFD format.
+                     command. Currently it only supports the DFD output format
+                     (http://en.wikipedia.org/wiki/Data_flow_diagram). Support
+                     for a Graphviz-like input format (instead of using the
+                     data structures in Haskell) is planned.
+homepage:            https://github.com/owickstrom/dataflow
 license:             MIT
 license-file:        LICENSE
 author:              Oskar Wickström
-maintainer:          oskar.wickstrom@live.com
--- copyright:
+maintainer:          oskar.wickstrom@gmail.com
+copyright:           oskar.wickstrom@gmail.com
+category:            Code Generation, Compiler, Graphs
+
 build-type:          Simple
--- extra-source-files:
+extra-source-files:  README.md,
+                     LICENSE,
+                     example/example.hs,
+                     example/example.svg,
+                     example/legend.hs,
+                     example/legend.svg
 cabal-version:       >=1.10
+
+source-repository head
+  type: git
+  location: git@github.com:owickstrom/dataflow.git
 
 library
   exposed-modules:
diff --git a/example/example.hs b/example/example.hs
new file mode 100644
--- /dev/null
+++ b/example/example.hs
@@ -0,0 +1,23 @@
+module Main where
+
+import DataFlow.Core
+import DataFlow.DFD
+
+main :: IO ()
+main = printDfd $
+  Diagram "My Diagram" [
+    TrustBoundary "browser" "Browser" [
+      Function "webapp" "Webapp"
+    ],
+    TrustBoundary "aws" "Amazon AWS" [
+      Function "server" "Web Server",
+      Database "logs" "Logs"
+    ],
+    InputOutput "analytics" "Google Analytics",
+
+    Flow "webapp" "server" "Request /" "",
+    Flow "server" "logs" "Log" "User IP",
+    Flow "server" "webapp" "Response" "User Profile",
+
+    Flow "webapp" "analytics" "Log" "Page Navigation"
+  ]
diff --git a/example/example.svg b/example/example.svg
new file mode 100644
--- /dev/null
+++ b/example/example.svg
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.38.0 (20140413.2041)
+ -->
+<!-- Title: My Diagram Pages: 1 -->
+<svg width="502pt" height="382pt"
+ viewBox="0.00 0.00 502.14 382.14" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 378.145)">
+<title>My Diagram</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-378.145 498.137,-378.145 498.137,4 -4,4"/>
+<text text-anchor="start" x="193.172" y="-355.145" font-family="sans-serif" font-weight="bold" font-size="20.00">My Diagram</text>
+<g id="clust1" class="cluster"><title>cluster_browser</title>
+<polygon fill="none" stroke="#4d4d4d" stroke-dasharray="5,2" points="8,-110.145 8,-230.145 109.78,-230.145 109.78,-110.145 8,-110.145"/>
+<text text-anchor="start" x="40.5524" y="-218.145" font-family="sans-serif" font-size="10.00" fill="#4d4d4d">Browser</text>
+</g>
+<g id="clust2" class="cluster"><title>cluster_aws</title>
+<polygon fill="none" stroke="#4d4d4d" stroke-dasharray="5,2" points="221.346,-190.145 221.346,-338.145 486.137,-338.145 486.137,-190.145 221.346,-190.145"/>
+<text text-anchor="start" x="323.078" y="-326.145" font-family="sans-serif" font-size="10.00" fill="#4d4d4d">Amazon AWS</text>
+</g>
+<!-- webapp -->
+<g id="node1" class="node"><title>webapp</title>
+<ellipse fill="none" stroke="black" cx="58.8898" cy="-161.145" rx="42.7799" ry="42.7799"/>
+<text text-anchor="start" x="32.9406" y="-157.945" font-family="sans-serif" font-weight="bold" font-size="14.00">Webapp</text>
+</g>
+<!-- server -->
+<g id="node2" class="node"><title>server</title>
+<ellipse fill="none" stroke="black" cx="285.985" cy="-255.145" rx="56.7776" ry="56.7776"/>
+<text text-anchor="start" x="249.153" y="-251.945" font-family="sans-serif" font-weight="bold" font-size="14.00">Web Server</text>
+</g>
+<!-- webapp&#45;&gt;server -->
+<g id="edge1" class="edge"><title>webapp&#45;&gt;server</title>
+<path fill="none" stroke="black" d="M98.8414,-177.411C133.323,-191.81 184.216,-213.063 224.076,-229.709"/>
+<polygon fill="black" stroke="black" points="222.86,-232.994 233.436,-233.618 225.557,-226.534 222.86,-232.994"/>
+<text text-anchor="middle" x="163.81" y="-224.545" font-family="sans-serif" font-size="12.00">webappserver&#45;&gt;</text>
+</g>
+<!-- analytics -->
+<g id="node4" class="node"><title>analytics</title>
+<polygon fill="none" stroke="black" stroke-width="2" points="346.274,-120.434 225.695,-120.434 225.695,0.144879 346.274,0.144879 346.274,-120.434"/>
+<text text-anchor="start" x="233.84" y="-56.9445" font-family="sans-serif" font-weight="bold" font-size="14.00">Google Analytics</text>
+</g>
+<!-- webapp&#45;&gt;analytics -->
+<g id="edge4" class="edge"><title>webapp&#45;&gt;analytics</title>
+<path fill="none" stroke="black" d="M84.2655,-126.418C94.0477,-114.842 106.228,-102.928 119.78,-95.1445 148.674,-78.5496 184.474,-69.7911 215.353,-65.1818"/>
+<polygon fill="black" stroke="black" points="216.188,-68.6001 225.619,-63.7724 215.236,-61.6651 216.188,-68.6001"/>
+<text text-anchor="start" x="144.799" y="-110.545" font-family="sans-serif" font-weight="bold" font-size="12.00">(4) Log</text>
+<text text-anchor="start" x="119.78" y="-98.5445" font-family="sans-serif" font-size="12.00">Page Navigation</text>
+</g>
+<!-- server&#45;&gt;webapp -->
+<g id="edge3" class="edge"><title>server&#45;&gt;webapp</title>
+<path fill="none" stroke="black" d="M256.434,-206.502C240.39,-181.104 221.004,-153.919 207.84,-147.145 177.542,-131.553 139.128,-135.775 109.036,-143.516"/>
+<polygon fill="black" stroke="black" points="107.874,-140.206 99.1738,-146.253 109.746,-146.952 107.874,-140.206"/>
+<text text-anchor="start" x="127.792" y="-162.545" font-family="sans-serif" font-weight="bold" font-size="12.00">(3) Response</text>
+<text text-anchor="start" x="132.468" y="-150.545" font-family="sans-serif" font-size="12.00">User Profile</text>
+</g>
+<!-- logs -->
+<g id="node3" class="node"><title>logs</title>
+<text text-anchor="start" x="434.958" y="-266.945" font-family="sans-serif" font-weight="bold" font-size="14.00">Logs</text>
+<polyline fill="none" stroke="black" points="430.137,-258.145 470.137,-258.145 "/>
+<polyline fill="none" stroke="black" points="470.137,-282.145 430.137,-282.145 "/>
+</g>
+<!-- server&#45;&gt;logs -->
+<g id="edge2" class="edge"><title>server&#45;&gt;logs</title>
+<path fill="none" stroke="black" d="M342.788,-260.307C365.568,-262.414 391.305,-264.795 411.754,-266.686"/>
+<polygon fill="black" stroke="black" points="411.642,-270.191 421.922,-267.627 412.287,-263.221 411.642,-270.191"/>
+<text text-anchor="start" x="365.122" y="-280.545" font-family="sans-serif" font-weight="bold" font-size="12.00">(2) Log</text>
+<text text-anchor="start" x="364.129" y="-268.545" font-family="sans-serif" font-size="12.00">User IP</text>
+</g>
+</g>
+</svg>
diff --git a/example/legend.hs b/example/legend.hs
new file mode 100644
--- /dev/null
+++ b/example/legend.hs
@@ -0,0 +1,17 @@
+module Main where
+
+import DataFlow.Core
+import DataFlow.DFD
+
+main :: IO ()
+main = printDfd $
+  Diagram "Legend" [
+    TrustBoundary "trust" "TrustBoundary" [
+      Function "function" "Function",
+      Database "database" "Database",
+      InputOutput "io" "InputOutput"
+    ],
+    Flow "function" "io" "Flow" "",
+    Flow "function" "database" "Flow" ""
+  ]
+
diff --git a/example/legend.svg b/example/legend.svg
new file mode 100644
--- /dev/null
+++ b/example/legend.svg
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.38.0 (20140413.2041)
+ -->
+<!-- Title: Legend Pages: 1 -->
+<svg width="297pt" height="284pt"
+ viewBox="0.00 0.00 296.55 284.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 280)">
+<title>Legend</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-280 292.552,-280 292.552,4 -4,4"/>
+<text text-anchor="start" x="110.907" y="-257" font-family="sans-serif" font-weight="bold" font-size="20.00">Legend</text>
+<g id="clust1" class="cluster"><title>cluster_trust</title>
+<polygon fill="none" stroke="#4d4d4d" stroke-dasharray="5,2" points="8,-8 8,-240 280.552,-240 280.552,-8 8,-8"/>
+<text text-anchor="start" x="111.666" y="-228" font-family="sans-serif" font-size="10.00" fill="#4d4d4d">TrustBoundary</text>
+</g>
+<!-- function -->
+<g id="node1" class="node"><title>function</title>
+<ellipse fill="none" stroke="black" cx="60.0255" cy="-129" rx="44.051" ry="44.051"/>
+<text text-anchor="start" x="33.1773" y="-125.8" font-family="sans-serif" font-weight="bold" font-size="14.00">Function</text>
+</g>
+<!-- database -->
+<g id="node2" class="node"><title>database</title>
+<text text-anchor="start" x="198.504" y="-30.8" font-family="sans-serif" font-weight="bold" font-size="14.00">Database</text>
+<polyline fill="none" stroke="black" points="193.469,-22 262.469,-22 "/>
+<polyline fill="none" stroke="black" points="262.469,-46 193.469,-46 "/>
+</g>
+<!-- function&#45;&gt;database -->
+<g id="edge2" class="edge"><title>function&#45;&gt;database</title>
+<path fill="none" stroke="black" d="M98.6807,-107.443C124.884,-92.4419 159.787,-72.4609 186.571,-57.1275"/>
+<polygon fill="black" stroke="black" points="188.379,-60.1253 195.319,-52.1195 184.901,-54.0504 188.379,-60.1253"/>
+<text text-anchor="start" x="122.051" y="-96.4" font-family="sans-serif" font-weight="bold" font-size="12.00">(2) Flow</text>
+</g>
+<!-- io -->
+<g id="node3" class="node"><title>io</title>
+<polygon fill="none" stroke="black" stroke-width="2" points="272.635,-213.665 183.304,-213.665 183.304,-124.335 272.635,-124.335 272.635,-213.665"/>
+<text text-anchor="start" x="191.387" y="-165.8" font-family="sans-serif" font-weight="bold" font-size="14.00">InputOutput</text>
+</g>
+<!-- function&#45;&gt;io -->
+<g id="edge1" class="edge"><title>function&#45;&gt;io</title>
+<path fill="none" stroke="black" d="M102.9,-139.094C124.311,-144.255 150.608,-150.593 173.472,-156.105"/>
+<polygon fill="black" stroke="black" points="172.828,-159.55 183.37,-158.49 174.468,-152.745 172.828,-159.55"/>
+<text text-anchor="start" x="122.051" y="-157.4" font-family="sans-serif" font-weight="bold" font-size="12.00">(1) Flow</text>
+</g>
+</g>
+</svg>
diff --git a/src/DataFlow/Core.hs b/src/DataFlow/Core.hs
--- a/src/DataFlow/Core.hs
+++ b/src/DataFlow/Core.hs
@@ -1,31 +1,71 @@
-module DataFlow.Core where
+module DataFlow.Core (
+  ID,
+  Name,
+  Operation,
+  Description,
+  Diagram(..),
+  Object(..),
+  Gen,
+  evalDiagram,
+  write,
+  writeln,
+  nextStep,
+  indent,
+  dedent,
+  withIndent,
+  blank,
+  label,
+  tag,
+  bold,
+  table,
+  tr,
+  td,
+  Enclosing(..),
+  objectWith,
+  attrs
+  ) where
 
 import Control.Monad.State
 import Control.Monad.Writer
 
+-- | An identifier corresponding to those in Graphviz.
 type ID = String
+-- | The name of a 'Diagram' or 'Object'.
 type Name = String
+-- | Operation heading.
 type Operation = String
+-- | Operation description.
 type Description = String
 
 -- | The top level diagram.
 data Diagram = Diagram Name [Object]
 
 -- | An object in a diagram.
-data Object = External ID Name
+data Object =
+            -- | A "Input" or "Output" in DFD.
+            InputOutput ID Name
+            -- | Surrounds other objects, denoting a boundary.
             | TrustBoundary ID Name [Object]
-            | Process ID Name
+            -- | A \"Function\" in DFD.
+            | Function ID Name
+            -- | A \"Database\" in DFD.
             | Database ID Name
-            | Edge ID ID Operation Description deriving (Show, Eq)
+            -- | Describes the flow of data between two objects.
+            | Flow ID ID Operation Description deriving (Show, Eq)
 
 type Indent = Int
 type IndentNext = Bool
 type Step = Int
 data GenState = GenState Indent IndentNext Step
 
--- | The monad stack for generating output based on Diagram.
+-- | The Gen represents some output generator that runs on a 'Diagram'..
 type Gen t = WriterT [String] (State GenState) t
 
+-- | Run the 'Gen' and get the output as a 'String'.
+evalDiagram :: Gen () -> String
+evalDiagram g = concat $ evalState (execWriterT g) (GenState 0 False 0)
+
+-- | Write a string to the output (no linefeed).
 write :: String -> Gen ()
 write s = do
   (GenState n indentNext step) <- lift get
@@ -34,6 +74,7 @@
     else tell [s]
   put $ GenState n False step
 
+-- | Write a string to the output (with linefeed).
 writeln :: String -> Gen ()
 writeln s = do
   write s
@@ -43,63 +84,80 @@
 incrStep :: Gen ()
 incrStep = modify $ \(GenState n indentNext s') -> GenState n indentNext (s' + 1)
 
+-- | Get the next \"step\" number (the order of flow arrows in the diagram).
 nextStep :: Gen Int
 nextStep = do
   incrStep
   (GenState _ _ s) <- lift get
   return s
 
+-- | Increase indent with 2 spaces.
 indent :: Gen ()
 indent = modify $ \(GenState n indentNext s) -> GenState (n + 2) indentNext s
 
+-- | Decrease indent with 2 spaces.
 dedent :: Gen ()
 dedent = modify $ \(GenState n indentNext s) -> GenState (n - 2) indentNext s
 
+-- | Indent the output of gen with 2 spaces.
 withIndent :: Gen () -> Gen ()
 withIndent gen = do
   indent
   gen
   dedent
 
+-- | Write a blank line.
 blank :: Gen ()
 blank = tell [""]
 
+-- | Write a label with the output of gen as its contents.
 label :: Gen () -> Gen ()
 label contents = do
   write "label = <"
   contents
   writeln ">;"
 
+-- | Write an HTML tag t with the output of gen as its contents.
 tag :: String -> String -> Gen () -> Gen ()
 tag t a contents = do
   write $ "<" ++ t ++ (if null a then "" else " " ++ a) ++ ">"
   contents
   write $ "</" ++ t ++ ">"
 
+-- | Write a \<b\> tag surrounding the output of another 'Gen'.
 bold :: Gen () -> Gen ()
 bold = tag "b" ""
 
+-- | Write a \<table\> tag, with attributes, surrounding the output of
+--   another 'Gen'.
 table :: String -> Gen () -> Gen ()
 table = tag "table"
 
+-- | Write a \<tr\> tag surrounding the output of another 'Gen'.
 tr :: Gen () -> Gen ()
 tr = tag "tr" ""
 
+-- | Write a \<td\> tag surrounding the output of another 'Gen'.
 td :: Gen () -> Gen ()
 td = tag "td" ""
 
-type Enclosing = (Char, Char)
-brackets, curlyBrackets :: Enclosing
-brackets = ('[', ']')
-curlyBrackets = ('{', '}')
+-- | The enclosing characters in a block.
+data Enclosing = Brackets | CurlyBrackets
 
+-- | Write an object with the given 'Enclosing' characters, 'ID' and 'Gen' as
+--   its contents.
 objectWith :: Enclosing -> ID -> Gen () -> Gen ()
-objectWith (before, after) id' attributes = do
-  blank
-  writeln $ id' ++ " " ++ [before]
-  withIndent attributes
-  writeln [after]
+objectWith enc id' attributes =
+  do
+    blank
+    writeln $ id' ++ " " ++ before enc
+    withIndent attributes
+    writeln $ after enc
+  where before Brackets = "["
+        before CurlyBrackets = "{"
+        after Brackets = "]"
+        after CurlyBrackets = "}"
 
+-- | Write an attributes declaration for the given 'ID'.
 attrs :: ID -> String -> Gen ()
-attrs id' = objectWith brackets id' . writeln
-
+attrs id' = objectWith Brackets id' . writeln
diff --git a/src/DataFlow/DFD.hs b/src/DataFlow/DFD.hs
--- a/src/DataFlow/DFD.hs
+++ b/src/DataFlow/DFD.hs
@@ -1,7 +1,5 @@
 module DataFlow.DFD where
 
-import Control.Monad.State
-import Control.Monad.Writer
 import DataFlow.Core
 
 -- | Type class for types that can be rendered as DFD.
@@ -9,7 +7,7 @@
   dfd :: t -> Gen ()
 
 instance DFD Object where
-  dfd (External id' name) = objectWith brackets id' $ do
+  dfd (InputOutput id' name) = objectWith Brackets id' $ do
     writeln "shape = square;"
     writeln "style = bold;"
     label $ bold $ write name
@@ -26,11 +24,11 @@
       writeln "graph[style = dashed, color=grey30];"
     writeln "}"
 
-  dfd (Process id' name) = objectWith brackets id' $ do
+  dfd (Function id' name) = objectWith Brackets id' $ do
     writeln "shape = circle;"
     label $ bold $ write name
 
-  dfd (Database id' name) = objectWith brackets id' $ do
+  dfd (Database id' name) = objectWith Brackets id' $ do
     label $
       table "sides=\"TB\" cellborder=\"0\"" $
         tr $
@@ -38,7 +36,7 @@
             bold $ write name
     writeln "shape = none;"
 
-  dfd (Edge i1 i2 operation description) = do
+  dfd (Flow i1 i2 operation description) = do
     step <- nextStep
     blank
     writeln $ i1 ++ " -> " ++ i2 ++ " ["
@@ -70,9 +68,9 @@
     writeln "}"
 
 -- | Generates the DFD output as a String.
-runDfd :: Diagram -> String
-runDfd diagram = concat $ evalState (execWriterT (dfd diagram)) (GenState 0 False 0)
+evalDfd :: Diagram -> String
+evalDfd = evalDiagram . dfd
 
 -- | Prints the DFD output to stdout.
 printDfd :: Diagram -> IO ()
-printDfd = putStr . runDfd
+printDfd = putStr . evalDfd
