diff --git a/example.json b/example.json
index 28042f411c7ad834eb447b8431dcb7efdfcffbb6..eb31b637426279b15d23335c61a7394798168dac 100644
--- a/example.json
+++ b/example.json
@@ -6,7 +6,8 @@
             "description": "A website for Pijar",
             "image": "https://pijar.com/static/images/pijar_pic.png",
             "css": [],
-            "js": []
+            "js": [],
+            "url_slug": ""
         },
         "body": [
             {
@@ -30,7 +31,8 @@
                     }
                 ]
             }
-        ]
+        ],
+        "sub_pages": []
     },
     "assets_index": {
         "images": [
diff --git a/model.dia b/model.dia
index 597d424aac6e8a01527dacf244a11593bfc25138..6a0f8a70ac906e294656c8f730efb72eb6e387cd 100644
Binary files a/model.dia and b/model.dia differ
diff --git a/src/app/args.rs b/src/app/args.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5deb639699e514f77384732bb7d6fa87ce8ac32a
--- /dev/null
+++ b/src/app/args.rs
@@ -0,0 +1,26 @@
+use std::path::PathBuf;
+use structopt::StructOpt;
+
+#[derive(Clone, StructOpt)]
+pub struct AppArgs {
+    #[structopt(short = "c", long = "ctx", default_value = "debug")]
+    pub context: String,
+
+    #[structopt(short = "d", long = "dir")]
+    pub app_storage_root: Option<PathBuf>,
+
+    #[structopt(long)]
+    pub load: Option<PathBuf>,
+
+    #[structopt(short, long, default_value = "localhost")]
+    pub host: String,
+
+    #[structopt(short, long, default_value = "8080")]
+    pub port: u16,
+
+    #[structopt(long = "ptls", default_value = "8443")]
+    pub port_tls: u16,
+
+    #[structopt(long = "certs_dir", default_value = "/etc/letsencrypt/live")]
+    pub ssl_certs_dir: PathBuf,
+}
diff --git a/src/app_state.rs b/src/app/config.rs
similarity index 65%
rename from src/app_state.rs
rename to src/app/config.rs
index 9cd3f677f9ce359e6426560aca90046b95f5e999..42ce77848de5707a0e0386bcb28a3281a5906607 100644
--- a/src/app_state.rs
+++ b/src/app/config.rs
@@ -1,3 +1,4 @@
+use crate::app::AppArgs;
 use std::path::PathBuf;
 use structopt::StructOpt;
 
@@ -7,30 +8,6 @@ pub enum AppContext {
     Production,
 }
 
-#[derive(Clone, StructOpt)]
-pub struct AppArgs {
-    #[structopt(short = "c", long = "ctx", default_value = "debug")]
-    pub context: String,
-
-    #[structopt(short = "d", long = "dir")]
-    pub app_storage_root: Option<PathBuf>,
-
-    #[structopt(long)]
-    pub load: Option<PathBuf>,
-
-    #[structopt(short, long, default_value = "localhost")]
-    pub host: String,
-
-    #[structopt(short, long, default_value = "8080")]
-    pub port: u16,
-
-    #[structopt(long = "ptls", default_value = "8443")]
-    pub port_tls: u16,
-
-    #[structopt(long = "certs_dir", default_value = "/etc/letsencrypt/live")]
-    pub ssl_certs_dir: PathBuf,
-}
-
 #[derive(Clone)]
 pub struct AppConfig {
     pub exec_path: PathBuf,
@@ -91,23 +68,3 @@ impl AppConfig {
         }
     }
 }
-
-#[derive(Clone)]
-pub struct AppState {
-    pub config: AppConfig,
-    pub preferences: AppPreference,
-    // authentication
-    // ...
-}
-
-#[derive(Clone)]
-pub struct AppPreference {}
-
-impl AppState {
-    pub fn new() -> Self {
-        AppState {
-            config: AppConfig::new(),
-            preferences: AppPreference {},
-        }
-    }
-}
diff --git a/src/app/mod.rs b/src/app/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e5bee9e4394401f26dfebbea5a6874ed924469c0
--- /dev/null
+++ b/src/app/mod.rs
@@ -0,0 +1,8 @@
+mod args;
+mod config;
+mod preferences;
+mod state;
+pub use args::*;
+pub use config::*;
+pub use preferences::*;
+pub use state::*;
diff --git a/src/app/preferences.rs b/src/app/preferences.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f097a658ef456d5f8d55e62499a90bf57ccc000f
--- /dev/null
+++ b/src/app/preferences.rs
@@ -0,0 +1,2 @@
+#[derive(Clone)]
+pub struct AppPreferences {}
diff --git a/src/app/state.rs b/src/app/state.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8d0dc75548ddbf3cc1fe0fed60a191b53af49768
--- /dev/null
+++ b/src/app/state.rs
@@ -0,0 +1,18 @@
+use crate::app::{AppConfig, AppPreferences};
+
+#[derive(Clone)]
+pub struct AppState {
+    pub config: AppConfig,
+    pub preferences: AppPreferences,
+    // authentication
+    // ...
+}
+
+impl AppState {
+    pub fn new() -> Self {
+        AppState {
+            config: AppConfig::new(),
+            preferences: AppPreferences {},
+        }
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index 1bd17254117dc4c20f4f1dbf45fd91b54af36052..c996be1f4371e3176c8f9ae649df1fb4db869b53 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,4 @@
-mod app_state;
+mod app;
 mod service;
 mod static_files;
 mod testing;
@@ -6,7 +6,7 @@ mod tls_config;
 mod website;
 use actix_web::{App, HttpServer};
 use actix_web_lab::middleware::RedirectHttps;
-use app_state::AppState;
+use app::AppState;
 use static_files::StaticFilesManager;
 use tls_config::tls_config;
 use website::WebSite;
diff --git a/src/service.rs b/src/service/mod.rs
similarity index 100%
rename from src/service.rs
rename to src/service/mod.rs
diff --git a/src/static_files.rs b/src/static_files.rs
index c4fbab6dd5d9e12d2babda955b05a58ffddd794d..4da405f36f67b5cfbac8679c84163147fa28c859 100644
--- a/src/static_files.rs
+++ b/src/static_files.rs
@@ -1,4 +1,4 @@
-use crate::app_state::AppState;
+use crate::app::AppState;
 use std::path::{Path, PathBuf};
 
 pub struct StaticFilesManager {
diff --git a/src/tls_config.rs b/src/tls_config.rs
index 61c23e834292fb988c87fdedf362cb88f7b3db56..b69f528a9c815460aee666b15dd4d0c50b5b6300 100644
--- a/src/tls_config.rs
+++ b/src/tls_config.rs
@@ -1,4 +1,4 @@
-use crate::app_state::AppConfig;
+use crate::app::AppConfig;
 
 pub fn tls_config(app_config: &AppConfig) -> rustls::ServerConfig {
     let certs_dir = app_config.ssl_certs_dir.clone();
diff --git a/src/website/html.rs b/src/website/html.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9385527ab52b428fa313ab6d25cc15249d5ecd58
--- /dev/null
+++ b/src/website/html.rs
@@ -0,0 +1,46 @@
+use crate::website::page::PageData;
+use regex::{Captures, Regex};
+use serde::{Deserialize, Serialize};
+
+pub const HTML_DOC_TEMPLATE: &'static str = "
+<html lang='{lang}'>
+<head>
+    <meta charset='UTF-8'>
+    <meta http-equiv='X-UA-Compatible' content='IE=edge'>
+    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
+    <meta name='description' content='{description}'>
+    <title>{title}</title>
+    <link rel='stylesheet' href='{css}'>
+</head>
+
+<body>
+    {body}
+</body>
+
+<script src='{js}'></script>
+
+</html>
+";
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct HtmlDoc(String);
+
+impl HtmlDoc {
+    pub fn from_page_data(page_data: &PageData) -> Self {
+        let re = Regex::new(r#"\{[a-z]+\}"#).unwrap();
+
+        let html = re
+            .replace_all(HTML_DOC_TEMPLATE, |captures: &Captures| {
+                let placeholder = captures.iter().next().unwrap().unwrap().as_str();
+                let placeholder = placeholder[1..placeholder.len() - 1].to_owned();
+                page_data.field_from_str_key(placeholder)
+            })
+            .to_string();
+
+        HtmlDoc(html)
+    }
+
+    pub fn to_string(&self) -> String {
+        self.0.clone()
+    }
+}
diff --git a/src/website/mod.rs b/src/website/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fcd415f3f758b45a0e5beb732b55fe40f845fcc3
--- /dev/null
+++ b/src/website/mod.rs
@@ -0,0 +1,5 @@
+mod html;
+mod page;
+mod website;
+
+pub use website::*;
diff --git a/src/website/page.rs b/src/website/page.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4f38e7a9cbcdd0321f948fe899d74000edeadc8d
--- /dev/null
+++ b/src/website/page.rs
@@ -0,0 +1,47 @@
+use crate::website::html::HtmlDoc;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct PageData {
+    pub title: String,
+    pub lang: String,
+    pub description: String,
+    pub slug: String,
+    pub html_body: String,
+    pub css_src: Option<String>,
+    pub js_src: Option<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct WebPage {
+    pub page_data: PageData,
+    pub html_doc: HtmlDoc,
+}
+
+impl PageData {
+    pub fn to_web_page(&self) -> WebPage {
+        WebPage {
+            page_data: self.clone(),
+            html_doc: HtmlDoc::from_page_data(&self),
+        }
+    }
+
+    pub fn field_from_str_key(&self, key: String) -> String {
+        match &key[..] {
+            "title" => self.title.to_owned(),
+            "lang" => self.lang.to_owned(),
+            "description" => self.description.to_owned(),
+            "slug" => self.slug.to_owned(),
+            "body" => self.html_body.to_owned(),
+            "css" => self.css_src.as_ref().unwrap_or(&String::new()).to_owned(),
+            "js" => self.js_src.as_ref().unwrap_or(&String::new()).to_owned(),
+            _ => String::new(),
+        }
+    }
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct PagesTree {
+    pub page_data: PageData,
+    pub sub_pages: Option<Vec<PagesTree>>,
+}
diff --git a/src/website.rs b/src/website/website.rs
similarity index 53%
rename from src/website.rs
rename to src/website/website.rs
index c65d5c57cc5002b4c474cb079be4bb3b369ddf00..6a6cb1f8d665c336760c3ac7e77191bc2804f1f8 100644
--- a/src/website.rs
+++ b/src/website/website.rs
@@ -1,87 +1,9 @@
-use crate::app_state::AppConfig;
-use regex::{Captures, Regex};
+use crate::app::AppConfig;
+use crate::website::page::{PagesTree, WebPage};
 use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
 use std::path::PathBuf;
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct PageData {
-    pub title: String,
-    pub lang: String,
-    pub description: String,
-    pub slug: String,
-    pub html_body: String,
-    pub css_src: Option<String>,
-    pub js_src: Option<String>,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct HtmlDoc(String);
-
-impl HtmlDoc {
-    fn load_template() -> String {
-        std::fs::read_to_string(
-            std::env::current_dir()
-                .unwrap()
-                .join("templates")
-                .join("html_doc.html"),
-        )
-        .expect("Missing html_doc template")
-    }
-
-    pub fn from_page_data(page_data: &PageData) -> Self {
-        let re = Regex::new(r#"\{[a-z]+\}"#).unwrap();
-
-        let html = re
-            .replace_all(&HtmlDoc::load_template(), |captures: &Captures| {
-                let placeholder = captures.iter().next().unwrap().unwrap().as_str();
-                let placeholder = placeholder[1..placeholder.len() - 1].to_owned();
-                page_data.field_from_str_key(placeholder)
-            })
-            .to_string();
-
-        HtmlDoc(html)
-    }
-
-    pub fn to_string(&self) -> String {
-        self.0.clone()
-    }
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct WebPage {
-    pub page_data: PageData,
-    pub html_doc: HtmlDoc,
-}
-
-impl PageData {
-    pub fn to_web_page(&self) -> WebPage {
-        WebPage {
-            page_data: self.clone(),
-            html_doc: HtmlDoc::from_page_data(&self),
-        }
-    }
-
-    pub fn field_from_str_key(&self, key: String) -> String {
-        match &key[..] {
-            "title" => self.title.to_owned(),
-            "lang" => self.lang.to_owned(),
-            "description" => self.description.to_owned(),
-            "slug" => self.slug.to_owned(),
-            "body" => self.html_body.to_owned(),
-            "css" => self.css_src.as_ref().unwrap_or(&String::new()).to_owned(),
-            "js" => self.js_src.as_ref().unwrap_or(&String::new()).to_owned(),
-            _ => String::new(),
-        }
-    }
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct PagesTree {
-    pub page_data: PageData,
-    pub sub_pages: Option<Vec<PagesTree>>,
-}
-
 #[derive(Debug, Serialize, Deserialize, Clone)]
 pub struct WebSite {
     pages_tree: PagesTree,
diff --git a/templates/html_doc.html b/templates/html_doc.html
deleted file mode 100644
index e8568983f7381e625c8a362998d811e2cf4fabb3..0000000000000000000000000000000000000000
--- a/templates/html_doc.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<html lang="{lang}">
-
-<head>
-    <meta charset="UTF-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <meta name="description" content="{description}">
-    <title>{title}</title>
-    <link rel="stylesheet" href="{css}">
-</head>
-
-<body>
-    {body}
-</body>
-<script src="{js}"></script>
-
-</html>
\ No newline at end of file
diff --git a/templates/new_website.json b/templates/new_website.json
index 92a78fa0bb711d3ca85824202be08689a336ee56..c6c845b183ec34d9d3fb805cb176ac14d07b0163 100644
--- a/templates/new_website.json
+++ b/templates/new_website.json
@@ -6,35 +6,5 @@
         "description": "A new website",
         "html_body": "<h1>New Website</h1>"
     },
-    "sub_pages": [
-        {
-            "page_data": {
-                "title": "A sub page",
-                "lang": "en",
-                "slug": "subpage",
-                "description": "A sub page of the new web site",
-                "html_body": "<h1>A sub page</h1>"
-            }
-        },
-        {
-            "page_data": {
-                "title": "Some other page",
-                "lang": "en",
-                "slug": "otherpage",
-                "description": "Some other page of the new web site",
-                "html_body": "<h1>Another page</h1>"
-            },
-            "sub_pages": [
-                {
-                    "page_data": {
-                        "title": "A sub page of the other page",
-                        "lang": "en",
-                        "slug": "othersubpage",
-                        "description": "A sub page of the other page of the new web site",
-                        "html_body": "<h1>A subpage</h1>"
-                    }
-                }
-            ]
-        }
-    ]
+    "sub_pages": []
 }
\ No newline at end of file