diff --git a/ma-impl.sublime-workspace b/ma-impl.sublime-workspace
index 24dbe8a68728954d801fc4a7d3dab7e87b4e8678..12a293e29ec5b63837bfab546db8548fe2b2fa01 100644
--- a/ma-impl.sublime-workspace
+++ b/ma-impl.sublime-workspace
@@ -271,49 +271,6 @@
 	},
 	"buffers":
 	[
-		{
-			"file": "vipra-ui/app/routes/articles.js",
-			"settings":
-			{
-				"buffer_size": 213,
-				"line_ending": "Unix"
-			}
-		},
-		{
-			"file": "vipra-ui/app/templates/articles.hbs",
-			"settings":
-			{
-				"buffer_size": 103,
-				"line_ending": "Unix"
-			}
-		},
-		{
-			"file": "vipra-ui/app/models/article.js",
-			"settings":
-			{
-				"buffer_size": 159,
-				"line_ending": "Unix"
-			}
-		},
-		{
-			"file": "vipra-ui/app/adapters/application.js",
-			"settings":
-			{
-				"buffer_size": 137,
-				"line_ending": "Unix"
-			}
-		},
-		{
-			"contents": "import DS from 'ember-data';\n\nexport default DS.RESTSerializer.extend({\n  \n});\n",
-			"file": "vipra-ui/app/serializers/application.js",
-			"file_size": 76,
-			"file_write_time": 130945949499834628,
-			"settings":
-			{
-				"buffer_size": 79,
-				"line_ending": "Unix"
-			}
-		}
 	],
 	"build_system": "",
 	"build_system_choices":
@@ -493,23 +450,22 @@
 	"expanded_folders":
 	[
 		"/home/eike/Repositories/fu/ss15/ma/impl",
-		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui",
-		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app",
-		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/adapters",
-		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/models",
-		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/routes",
-		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/serializers",
-		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/templates",
-		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/templates/components",
-		"/home/eike/Repositories/fu/ss15/ma/impl/vm/config"
+		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui"
 	],
 	"file_history":
 	[
-		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/app.js",
+		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/bower.json",
+		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/bower_components/ember/ember.js",
+		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/routes/articles.js",
+		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/package.json",
 		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/templates/articles.hbs",
+		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/models/article.js",
+		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/router.js",
+		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/adapters/application.js",
+		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/serializers/application.js",
+		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/app.js",
 		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/templates/application.hbs",
 		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/templates/index.hbs",
-		"/home/eike/Repositories/fu/ss15/ma/impl/vipra-ui/app/router.js",
 		"/home/eike/Repositories/fu/ss15/ma/impl/vm/bootstrap.sh",
 		"/home/eike/Repositories/fu/ss15/ma/doc/thesis/thesis.tex",
 		"/home/eike/Repositories/fu/ss15/ma/impl/Vagrantfile",
@@ -900,484 +856,8 @@
 	"groups":
 	[
 		{
-			"selected": 0,
 			"sheets":
 			[
-				{
-					"buffer": 0,
-					"file": "vipra-ui/app/routes/articles.js",
-					"semi_transient": false,
-					"settings":
-					{
-						"buffer_size": 213,
-						"regions":
-						{
-						},
-						"selection":
-						[
-							[
-								213,
-								213
-							]
-						],
-						"settings":
-						{
-							"BracketHighlighterBusy": false,
-							"bh_regions":
-							[
-								"bh_curly",
-								"bh_curly_center",
-								"bh_curly_open",
-								"bh_curly_close",
-								"bh_curly_content",
-								"bh_c_define",
-								"bh_c_define_center",
-								"bh_c_define_open",
-								"bh_c_define_close",
-								"bh_c_define_content",
-								"bh_double_quote",
-								"bh_double_quote_center",
-								"bh_double_quote_open",
-								"bh_double_quote_close",
-								"bh_double_quote_content",
-								"bh_tag",
-								"bh_tag_center",
-								"bh_tag_open",
-								"bh_tag_close",
-								"bh_tag_content",
-								"bh_default",
-								"bh_default_center",
-								"bh_default_open",
-								"bh_default_close",
-								"bh_default_content",
-								"bh_angle",
-								"bh_angle_center",
-								"bh_angle_open",
-								"bh_angle_close",
-								"bh_angle_content",
-								"bh_unmatched",
-								"bh_unmatched_center",
-								"bh_unmatched_open",
-								"bh_unmatched_close",
-								"bh_unmatched_content",
-								"bh_single_quote",
-								"bh_single_quote_center",
-								"bh_single_quote_open",
-								"bh_single_quote_close",
-								"bh_single_quote_content",
-								"bh_round",
-								"bh_round_center",
-								"bh_round_open",
-								"bh_round_close",
-								"bh_round_content",
-								"bh_regex",
-								"bh_regex_center",
-								"bh_regex_open",
-								"bh_regex_close",
-								"bh_regex_content",
-								"bh_square",
-								"bh_square_center",
-								"bh_square_open",
-								"bh_square_close",
-								"bh_square_content"
-							],
-							"editorconfig": true,
-							"ensure_newline_at_eof_on_save": true,
-							"incomplete_sync": null,
-							"remote_loading": false,
-							"synced": false,
-							"syntax": "Packages/JavaScript/JavaScript.sublime-syntax",
-							"tab_size": 2,
-							"translate_tabs_to_spaces": true,
-							"trim_trailing_white_space_on_save": true
-						},
-						"translation.x": 0.0,
-						"translation.y": 0.0,
-						"zoom_level": 1.0
-					},
-					"stack_index": 0,
-					"type": "text"
-				},
-				{
-					"buffer": 1,
-					"file": "vipra-ui/app/templates/articles.hbs",
-					"semi_transient": false,
-					"settings":
-					{
-						"buffer_size": 103,
-						"regions":
-						{
-						},
-						"selection":
-						[
-							[
-								80,
-								80
-							]
-						],
-						"settings":
-						{
-							"BracketHighlighterBusy": false,
-							"bh_regions":
-							[
-								"bh_curly",
-								"bh_curly_center",
-								"bh_curly_open",
-								"bh_curly_close",
-								"bh_curly_content",
-								"bh_c_define",
-								"bh_c_define_center",
-								"bh_c_define_open",
-								"bh_c_define_close",
-								"bh_c_define_content",
-								"bh_double_quote",
-								"bh_double_quote_center",
-								"bh_double_quote_open",
-								"bh_double_quote_close",
-								"bh_double_quote_content",
-								"bh_tag",
-								"bh_tag_center",
-								"bh_tag_open",
-								"bh_tag_close",
-								"bh_tag_content",
-								"bh_default",
-								"bh_default_center",
-								"bh_default_open",
-								"bh_default_close",
-								"bh_default_content",
-								"bh_angle",
-								"bh_angle_center",
-								"bh_angle_open",
-								"bh_angle_close",
-								"bh_angle_content",
-								"bh_unmatched",
-								"bh_unmatched_center",
-								"bh_unmatched_open",
-								"bh_unmatched_close",
-								"bh_unmatched_content",
-								"bh_single_quote",
-								"bh_single_quote_center",
-								"bh_single_quote_open",
-								"bh_single_quote_close",
-								"bh_single_quote_content",
-								"bh_round",
-								"bh_round_center",
-								"bh_round_open",
-								"bh_round_close",
-								"bh_round_content",
-								"bh_regex",
-								"bh_regex_center",
-								"bh_regex_open",
-								"bh_regex_close",
-								"bh_regex_content",
-								"bh_square",
-								"bh_square_center",
-								"bh_square_open",
-								"bh_square_close",
-								"bh_square_content"
-							],
-							"editorconfig": true,
-							"ensure_newline_at_eof_on_save": false,
-							"incomplete_sync": null,
-							"remote_loading": false,
-							"synced": false,
-							"syntax": "Packages/Handlebars/grammars/Handlebars.tmLanguage",
-							"tab_size": 2,
-							"translate_tabs_to_spaces": true,
-							"trim_trailing_white_space_on_save": true
-						},
-						"translation.x": 0.0,
-						"translation.y": 0.0,
-						"zoom_level": 1.0
-					},
-					"stack_index": 1,
-					"type": "text"
-				},
-				{
-					"buffer": 2,
-					"file": "vipra-ui/app/models/article.js",
-					"semi_transient": false,
-					"settings":
-					{
-						"buffer_size": 159,
-						"regions":
-						{
-						},
-						"selection":
-						[
-							[
-								62,
-								62
-							]
-						],
-						"settings":
-						{
-							"BracketHighlighterBusy": false,
-							"bh_regions":
-							[
-								"bh_curly",
-								"bh_curly_center",
-								"bh_curly_open",
-								"bh_curly_close",
-								"bh_curly_content",
-								"bh_c_define",
-								"bh_c_define_center",
-								"bh_c_define_open",
-								"bh_c_define_close",
-								"bh_c_define_content",
-								"bh_double_quote",
-								"bh_double_quote_center",
-								"bh_double_quote_open",
-								"bh_double_quote_close",
-								"bh_double_quote_content",
-								"bh_tag",
-								"bh_tag_center",
-								"bh_tag_open",
-								"bh_tag_close",
-								"bh_tag_content",
-								"bh_default",
-								"bh_default_center",
-								"bh_default_open",
-								"bh_default_close",
-								"bh_default_content",
-								"bh_angle",
-								"bh_angle_center",
-								"bh_angle_open",
-								"bh_angle_close",
-								"bh_angle_content",
-								"bh_unmatched",
-								"bh_unmatched_center",
-								"bh_unmatched_open",
-								"bh_unmatched_close",
-								"bh_unmatched_content",
-								"bh_single_quote",
-								"bh_single_quote_center",
-								"bh_single_quote_open",
-								"bh_single_quote_close",
-								"bh_single_quote_content",
-								"bh_round",
-								"bh_round_center",
-								"bh_round_open",
-								"bh_round_close",
-								"bh_round_content",
-								"bh_regex",
-								"bh_regex_center",
-								"bh_regex_open",
-								"bh_regex_close",
-								"bh_regex_content",
-								"bh_square",
-								"bh_square_center",
-								"bh_square_open",
-								"bh_square_close",
-								"bh_square_content"
-							],
-							"editorconfig": true,
-							"ensure_newline_at_eof_on_save": true,
-							"incomplete_sync": null,
-							"remote_loading": false,
-							"synced": false,
-							"syntax": "Packages/JavaScript/JavaScript.sublime-syntax",
-							"tab_size": 2,
-							"translate_tabs_to_spaces": true,
-							"trim_trailing_white_space_on_save": true
-						},
-						"translation.x": 0.0,
-						"translation.y": 0.0,
-						"zoom_level": 1.0
-					},
-					"stack_index": 2,
-					"type": "text"
-				},
-				{
-					"buffer": 3,
-					"file": "vipra-ui/app/adapters/application.js",
-					"semi_transient": false,
-					"settings":
-					{
-						"buffer_size": 137,
-						"regions":
-						{
-						},
-						"selection":
-						[
-							[
-								132,
-								132
-							]
-						],
-						"settings":
-						{
-							"BracketHighlighterBusy": false,
-							"bh_regions":
-							[
-								"bh_curly",
-								"bh_curly_center",
-								"bh_curly_open",
-								"bh_curly_close",
-								"bh_curly_content",
-								"bh_c_define",
-								"bh_c_define_center",
-								"bh_c_define_open",
-								"bh_c_define_close",
-								"bh_c_define_content",
-								"bh_double_quote",
-								"bh_double_quote_center",
-								"bh_double_quote_open",
-								"bh_double_quote_close",
-								"bh_double_quote_content",
-								"bh_tag",
-								"bh_tag_center",
-								"bh_tag_open",
-								"bh_tag_close",
-								"bh_tag_content",
-								"bh_default",
-								"bh_default_center",
-								"bh_default_open",
-								"bh_default_close",
-								"bh_default_content",
-								"bh_angle",
-								"bh_angle_center",
-								"bh_angle_open",
-								"bh_angle_close",
-								"bh_angle_content",
-								"bh_unmatched",
-								"bh_unmatched_center",
-								"bh_unmatched_open",
-								"bh_unmatched_close",
-								"bh_unmatched_content",
-								"bh_single_quote",
-								"bh_single_quote_center",
-								"bh_single_quote_open",
-								"bh_single_quote_close",
-								"bh_single_quote_content",
-								"bh_round",
-								"bh_round_center",
-								"bh_round_open",
-								"bh_round_close",
-								"bh_round_content",
-								"bh_regex",
-								"bh_regex_center",
-								"bh_regex_open",
-								"bh_regex_close",
-								"bh_regex_content",
-								"bh_square",
-								"bh_square_center",
-								"bh_square_open",
-								"bh_square_close",
-								"bh_square_content"
-							],
-							"editorconfig": true,
-							"ensure_newline_at_eof_on_save": true,
-							"incomplete_sync": null,
-							"remote_loading": false,
-							"synced": false,
-							"syntax": "Packages/JavaScript/JavaScript.sublime-syntax",
-							"tab_size": 2,
-							"translate_tabs_to_spaces": true,
-							"trim_trailing_white_space_on_save": true
-						},
-						"translation.x": 0.0,
-						"translation.y": 0.0,
-						"zoom_level": 1.0
-					},
-					"stack_index": 4,
-					"type": "text"
-				},
-				{
-					"buffer": 4,
-					"file": "vipra-ui/app/serializers/application.js",
-					"semi_transient": false,
-					"settings":
-					{
-						"buffer_size": 79,
-						"regions":
-						{
-						},
-						"selection":
-						[
-							[
-								74,
-								74
-							]
-						],
-						"settings":
-						{
-							"BracketHighlighterBusy": false,
-							"bh_regions":
-							[
-								"bh_curly",
-								"bh_curly_center",
-								"bh_curly_open",
-								"bh_curly_close",
-								"bh_curly_content",
-								"bh_c_define",
-								"bh_c_define_center",
-								"bh_c_define_open",
-								"bh_c_define_close",
-								"bh_c_define_content",
-								"bh_double_quote",
-								"bh_double_quote_center",
-								"bh_double_quote_open",
-								"bh_double_quote_close",
-								"bh_double_quote_content",
-								"bh_tag",
-								"bh_tag_center",
-								"bh_tag_open",
-								"bh_tag_close",
-								"bh_tag_content",
-								"bh_default",
-								"bh_default_center",
-								"bh_default_open",
-								"bh_default_close",
-								"bh_default_content",
-								"bh_angle",
-								"bh_angle_center",
-								"bh_angle_open",
-								"bh_angle_close",
-								"bh_angle_content",
-								"bh_unmatched",
-								"bh_unmatched_center",
-								"bh_unmatched_open",
-								"bh_unmatched_close",
-								"bh_unmatched_content",
-								"bh_single_quote",
-								"bh_single_quote_center",
-								"bh_single_quote_open",
-								"bh_single_quote_close",
-								"bh_single_quote_content",
-								"bh_round",
-								"bh_round_center",
-								"bh_round_open",
-								"bh_round_close",
-								"bh_round_content",
-								"bh_regex",
-								"bh_regex_center",
-								"bh_regex_open",
-								"bh_regex_close",
-								"bh_regex_content",
-								"bh_square",
-								"bh_square_center",
-								"bh_square_open",
-								"bh_square_close",
-								"bh_square_content"
-							],
-							"editorconfig": true,
-							"ensure_newline_at_eof_on_save": true,
-							"incomplete_sync": null,
-							"remote_loading": false,
-							"synced": false,
-							"syntax": "Packages/JavaScript/JavaScript.sublime-syntax",
-							"tab_size": 2,
-							"translate_tabs_to_spaces": true,
-							"trim_trailing_white_space_on_save": true
-						},
-						"translation.x": 0.0,
-						"translation.y": 0.0,
-						"zoom_level": 1.0
-					},
-					"stack_index": 3,
-					"type": "text"
-				}
 			]
 		}
 	],
diff --git a/vipra-cmd/cmd/.classpath b/vipra-cmd/.classpath
similarity index 100%
rename from vipra-cmd/cmd/.classpath
rename to vipra-cmd/.classpath
diff --git a/vipra-cmd/cmd/.gitignore b/vipra-cmd/.gitignore
similarity index 100%
rename from vipra-cmd/cmd/.gitignore
rename to vipra-cmd/.gitignore
diff --git a/vipra-cmd/cmd/.project b/vipra-cmd/.project
similarity index 94%
rename from vipra-cmd/cmd/.project
rename to vipra-cmd/.project
index f6babc09643e3cc5e65f288e4a18ccfdfade64ed..7457abc9569d7378c1550159176d5982a81e3fe9 100644
--- a/vipra-cmd/cmd/.project
+++ b/vipra-cmd/.project
@@ -37,7 +37,7 @@
 		<link>
 			<name>src/main/resources/config.properties</name>
 			<type>1</type>
-			<locationURI>PARENT-2-PROJECT_LOC/vipra-config/config.properties</locationURI>
+			<locationURI>PARENT-1-PROJECT_LOC/vipra-config/config.properties</locationURI>
 		</link>
 	</linkedResources>
 </projectDescription>
diff --git a/vipra-cmd/cmd/.settings/org.eclipse.core.resources.prefs b/vipra-cmd/.settings/org.eclipse.core.resources.prefs
similarity index 100%
rename from vipra-cmd/cmd/.settings/org.eclipse.core.resources.prefs
rename to vipra-cmd/.settings/org.eclipse.core.resources.prefs
diff --git a/vipra-cmd/cmd/.settings/org.eclipse.jdt.core.prefs b/vipra-cmd/.settings/org.eclipse.jdt.core.prefs
similarity index 100%
rename from vipra-cmd/cmd/.settings/org.eclipse.jdt.core.prefs
rename to vipra-cmd/.settings/org.eclipse.jdt.core.prefs
diff --git a/vipra-cmd/cmd/.settings/org.eclipse.m2e.core.prefs b/vipra-cmd/.settings/org.eclipse.m2e.core.prefs
similarity index 100%
rename from vipra-cmd/cmd/.settings/org.eclipse.m2e.core.prefs
rename to vipra-cmd/.settings/org.eclipse.m2e.core.prefs
diff --git a/vipra-cmd/cmd/.settings/org.eclipse.wst.common.component b/vipra-cmd/.settings/org.eclipse.wst.common.component
similarity index 100%
rename from vipra-cmd/cmd/.settings/org.eclipse.wst.common.component
rename to vipra-cmd/.settings/org.eclipse.wst.common.component
diff --git a/vipra-cmd/cmd/.settings/org.eclipse.wst.common.project.facet.core.xml b/vipra-cmd/.settings/org.eclipse.wst.common.project.facet.core.xml
similarity index 100%
rename from vipra-cmd/cmd/.settings/org.eclipse.wst.common.project.facet.core.xml
rename to vipra-cmd/.settings/org.eclipse.wst.common.project.facet.core.xml
diff --git a/vipra-cmd/cmd/pom.xml b/vipra-cmd/pom.xml
similarity index 100%
rename from vipra-cmd/cmd/pom.xml
rename to vipra-cmd/pom.xml
diff --git a/vipra-cmd/cmd/src/main/java/de/vipra/cmd/CmdOptions.java b/vipra-cmd/src/main/java/de/vipra/cmd/CmdOptions.java
similarity index 100%
rename from vipra-cmd/cmd/src/main/java/de/vipra/cmd/CmdOptions.java
rename to vipra-cmd/src/main/java/de/vipra/cmd/CmdOptions.java
diff --git a/vipra-cmd/cmd/src/main/java/de/vipra/cmd/Configuration.java b/vipra-cmd/src/main/java/de/vipra/cmd/Configuration.java
similarity index 100%
rename from vipra-cmd/cmd/src/main/java/de/vipra/cmd/Configuration.java
rename to vipra-cmd/src/main/java/de/vipra/cmd/Configuration.java
diff --git a/vipra-cmd/cmd/src/main/java/de/vipra/cmd/ExecutionException.java b/vipra-cmd/src/main/java/de/vipra/cmd/ExecutionException.java
similarity index 100%
rename from vipra-cmd/cmd/src/main/java/de/vipra/cmd/ExecutionException.java
rename to vipra-cmd/src/main/java/de/vipra/cmd/ExecutionException.java
diff --git a/vipra-cmd/cmd/src/main/java/de/vipra/cmd/Main.java b/vipra-cmd/src/main/java/de/vipra/cmd/Main.java
similarity index 100%
rename from vipra-cmd/cmd/src/main/java/de/vipra/cmd/Main.java
rename to vipra-cmd/src/main/java/de/vipra/cmd/Main.java
diff --git a/vipra-cmd/cmd/src/main/java/de/vipra/cmd/MongoDB.java b/vipra-cmd/src/main/java/de/vipra/cmd/MongoDB.java
similarity index 100%
rename from vipra-cmd/cmd/src/main/java/de/vipra/cmd/MongoDB.java
rename to vipra-cmd/src/main/java/de/vipra/cmd/MongoDB.java
diff --git a/vipra-cmd/cmd/src/main/java/de/vipra/cmd/lda/LDAWrapper.java b/vipra-cmd/src/main/java/de/vipra/cmd/lda/LDAWrapper.java
similarity index 100%
rename from vipra-cmd/cmd/src/main/java/de/vipra/cmd/lda/LDAWrapper.java
rename to vipra-cmd/src/main/java/de/vipra/cmd/lda/LDAWrapper.java
diff --git a/vipra-cmd/cmd/src/main/java/de/vipra/cmd/option/ImportOption.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportOption.java
similarity index 100%
rename from vipra-cmd/cmd/src/main/java/de/vipra/cmd/option/ImportOption.java
rename to vipra-cmd/src/main/java/de/vipra/cmd/option/ImportOption.java
diff --git a/vipra-cmd/cmd/src/main/resources/log4j2.xml b/vipra-cmd/src/main/resources/log4j2.xml
similarity index 100%
rename from vipra-cmd/cmd/src/main/resources/log4j2.xml
rename to vipra-cmd/src/main/resources/log4j2.xml
diff --git a/vipra-cmd/cmd/src/main/resources/log4j2dev.xml b/vipra-cmd/src/main/resources/log4j2dev.xml
similarity index 100%
rename from vipra-cmd/cmd/src/main/resources/log4j2dev.xml
rename to vipra-cmd/src/main/resources/log4j2dev.xml
diff --git a/vipra-rest/src/main/java/de/vipra/rest/Application.java b/vipra-rest/src/main/java/de/vipra/rest/Application.java
index 7e667e104990fdac4a6ace7172911f04ac82ce1f..632b4c8fc73b985bd0f7063bf8901f32f276fded 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/Application.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/Application.java
@@ -3,14 +3,18 @@ package de.vipra.rest;
 import org.glassfish.jersey.jackson.JacksonFeature;
 import org.glassfish.jersey.server.ResourceConfig;
 
+import de.vipra.rest.provider.APIRequestFilter;
+import de.vipra.rest.provider.CORSResponseFilter;
 import de.vipra.rest.provider.ObjectMapperProvider;
 
 public class Application extends ResourceConfig {
 
 	public Application() {
 		packages("de.vipra.rest.resource");
-		register(ObjectMapperProvider.class);
 		register(JacksonFeature.class);
+		register(CORSResponseFilter.class);
+		register(ObjectMapperProvider.class);
+		register(APIRequestFilter.class);
 	}
 
 }
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/APIVersion.java b/vipra-rest/src/main/java/de/vipra/rest/model/APIVersion.java
index 1e2495d8cf03cfd740304353c0fbed586f2fb439..6e80449fd3cfa916a28751edf259382151fe73e0 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/model/APIVersion.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/model/APIVersion.java
@@ -1,18 +1,7 @@
 package de.vipra.rest.model;
 
-import javax.xml.bind.annotation.XmlRootElement;
-
-@XmlRootElement
 public class APIVersion {
 
-	private String version = "1.0";
-
-	public String getVersion() {
-		return version;
-	}
-
-	public void setVersion(String version) {
-		this.version = version;
-	}
+	public final String version = "1.0";
 
 }
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/Article.java b/vipra-rest/src/main/java/de/vipra/rest/model/Article.java
new file mode 100644
index 0000000000000000000000000000000000000000..45fb190f8a95f07ce6c73bf98ee237a61b44c82a
--- /dev/null
+++ b/vipra-rest/src/main/java/de/vipra/rest/model/Article.java
@@ -0,0 +1,83 @@
+package de.vipra.rest.model;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import org.bson.Document;
+import org.bson.types.ObjectId;
+
+public class Article extends Model {
+
+	private String title;
+	private String text;
+	private String url;
+	private Date date;
+
+	public Article() {
+		super(Article.class);
+	}
+
+	public Article(Document document) {
+		this();
+		fromDocument(document);
+	}
+
+	public String getTitle() {
+		return title;
+	}
+
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	public String getText() {
+		return text;
+	}
+
+	public void setText(String text) {
+		this.text = text;
+	}
+
+	public String getUrl() {
+		return url;
+	}
+
+	public void setUrl(String url) {
+		this.url = url;
+	}
+
+	public Date getDate() {
+		return date;
+	}
+
+	public void setDate(Date date) {
+		this.date = date;
+	}
+
+	@Override
+	public Document toDocument() {
+		Document doc = new Document("title", title).append("text", text).append("url", url).append("date", date);
+		if (id != null) {
+			doc.append("_id", new ObjectId(id));
+		}
+		return doc;
+	}
+
+	@Override
+	public void fromDocument(Document document) {
+		id = document.getObjectId("_id").toString();
+		title = document.getString("title");
+		text = document.getString("text");
+		url = document.getString("url");
+		date = document.getDate("date");
+	}
+
+	public static ArrayList<Article> fromDocuments(final ArrayList<Document> docs) {
+		ArrayList<Article> articles = new ArrayList<Article>();
+		for (Document doc : docs) {
+			articles.add(new Article(doc));
+		}
+		return articles;
+	}
+
+}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/Articles.java b/vipra-rest/src/main/java/de/vipra/rest/model/Articles.java
deleted file mode 100644
index 6142e729a9b760415fa5f840ba0e0b312bd72584..0000000000000000000000000000000000000000
--- a/vipra-rest/src/main/java/de/vipra/rest/model/Articles.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package de.vipra.rest.model;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.bson.Document;
-import org.bson.types.ObjectId;
-
-public class Articles implements Model, Links {
-
-	private String type = Articles.class.getSimpleName().toLowerCase();
-	private String id;
-	private String title;
-	private String text;
-	private String url;
-	private Date date;
-	private Map<String, Link> links;
-
-	public Articles() {}
-
-	public Articles(Document document) {
-		fromDocument(document);
-	}
-
-	public String getType() {
-		return type;
-	}
-
-	public void setType(String type) {
-		this.type = type;
-	}
-
-	public String getId() {
-		return id;
-	}
-
-	public void setId(String id) {
-		this.id = id;
-	}
-
-	public String getTitle() {
-		return title;
-	}
-
-	public void setTitle(String title) {
-		this.title = title;
-	}
-
-	public String getText() {
-		return text;
-	}
-
-	public void setText(String text) {
-		this.text = text;
-	}
-
-	public String getUrl() {
-		return url;
-	}
-
-	public void setUrl(String url) {
-		this.url = url;
-	}
-
-	public Date getDate() {
-		return date;
-	}
-
-	public void setDate(Date date) {
-		this.date = date;
-	}
-
-	@Override
-	public Document toDocument() {
-		Document doc = new Document("title", title).append("text", text).append("url", url).append("date", date);
-		if (id != null) {
-			doc.append("_id", new ObjectId(id));
-		}
-		return doc;
-	}
-
-	@Override
-	public void fromDocument(Document document) {
-		id = document.getObjectId("_id").toString();
-		title = document.getString("title");
-		text = document.getString("text");
-		url = document.getString("url");
-		date = document.getDate("date");
-	}
-
-	public static ArrayList<Articles> fromDocuments(final ArrayList<Document> docs) {
-		ArrayList<Articles> articles = new ArrayList<Articles>();
-		for (Document doc : docs) {
-			articles.add(new Articles(doc));
-		}
-		return articles;
-	}
-
-	@Override
-	public URI getURI(URI base) {
-		return Articles.getURI(base, id);
-	}
-
-	@Override
-	public Map<String, Link> getLinks() {
-		return links;
-	}
-
-	@Override
-	public void setLinks(Map<String, Link> links) {
-		this.links = links;
-	}
-
-	@Override
-	public void addLink(String name, Link link) {
-		if (links == null) {
-			links = new HashMap<>();
-		}
-		links.put(name, link);
-	}
-
-	@Override
-	public void setSelf(URI base) {
-		addLink("self", new Link(getURI(base, id).toString()));
-	}
-
-	public static URI getURI(URI base, String id) {
-		try {
-			return new URI(base.toString() + "/" + id);
-		} catch (URISyntaxException e) {
-			return null;
-		}
-	}
-
-}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/Link.java b/vipra-rest/src/main/java/de/vipra/rest/model/Link.java
deleted file mode 100644
index e3233dacdd49bb6980ae727d58ee9e39cfddc4fe..0000000000000000000000000000000000000000
--- a/vipra-rest/src/main/java/de/vipra/rest/model/Link.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package de.vipra.rest.model;
-
-public class Link {
-
-	private String href;
-	private Object meta;
-
-	public Link() {}
-
-	public Link(String href) {
-		setHref(href);
-	}
-
-	public String getHref() {
-		return href;
-	}
-
-	public void setHref(String href) {
-		this.href = href;
-	}
-
-	public Object getMeta() {
-		return meta;
-	}
-
-	public void setMeta(Object meta) {
-		this.meta = meta;
-	}
-
-}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/Links.java b/vipra-rest/src/main/java/de/vipra/rest/model/Links.java
deleted file mode 100644
index 939277d28519a3ab2cfc06326aa2faea8f8de8fa..0000000000000000000000000000000000000000
--- a/vipra-rest/src/main/java/de/vipra/rest/model/Links.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package de.vipra.rest.model;
-
-import java.net.URI;
-import java.util.Map;
-
-public interface Links {
-
-	Map<String, Link> getLinks();
-
-	void setLinks(Map<String, Link> links);
-
-	void addLink(String name, Link link);
-
-	void setSelf(URI base);
-
-}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/Meta.java b/vipra-rest/src/main/java/de/vipra/rest/model/Meta.java
deleted file mode 100644
index 3570a5ea4d0955cf902c7862b911fe7afea5ed10..0000000000000000000000000000000000000000
--- a/vipra-rest/src/main/java/de/vipra/rest/model/Meta.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package de.vipra.rest.model;
-
-import java.util.Map;
-
-public interface Meta {
-
-	Map<String, Object> getMeta();
-
-	void setMeta(Map<String, Object> meta);
-
-	void addMeta(String name, Object meta);
-
-}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/Model.java b/vipra-rest/src/main/java/de/vipra/rest/model/Model.java
index 0de5473c28cb06cfd238823ad1ae45a2129944b5..bbc7690929343e7a9d6e7dbc349b43783aa8a756 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/model/Model.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/model/Model.java
@@ -2,15 +2,64 @@ package de.vipra.rest.model;
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.bson.Document;
 
-public interface Model {
+public abstract class Model {
 
-	Document toDocument();
+	protected String id;
+	protected String type;
+	protected Map<String, String> links;
 
-	void fromDocument(Document document);
+	public Model() {}
 
-	URI getURI(URI base) throws URISyntaxException;
+	public Model(Class<?> clazz) {
+		this.type = clazz.getSimpleName().toLowerCase();
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public Map<String, String> getLinks() {
+		return links;
+	}
+
+	public void setLinks(Map<String, String> links) {
+		this.links = links;
+	}
+
+	public void addLink(String name, String link) {
+		if (links == null) {
+			links = new HashMap<>();
+		}
+		links.put(name, link);
+	}
+
+	public void setSelf(URI base) {
+		addLink("self", getURI(base).toString());
+	}
+
+	abstract Document toDocument();
+
+	abstract void fromDocument(Document document);
+
+	public URI getURI(URI base) {
+		try {
+			return new URI(base.toString() + "/" + id);
+		} catch (URISyntaxException e) {
+			return null;
+		}
+	}
 
 }
diff --git a/vipra-rest/src/main/java/de/vipra/rest/model/ResponseWrapper.java b/vipra-rest/src/main/java/de/vipra/rest/model/ResponseWrapper.java
index e58bcef4097ba99ed60235e8239af99b4261cba0..ac96167ba1e4c00adb3d2be13adac62711e379c7 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/model/ResponseWrapper.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/model/ResponseWrapper.java
@@ -1,17 +1,16 @@
 package de.vipra.rest.model;
 
-import java.net.URI;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-public class ResponseWrapper<T> implements Links {
+public class ResponseWrapper<T> {
 
-	private Map<String, Link> links;
+	private Map<String, String> links;
 	private T data;
 	private List<APIError> errors;
-	private APIVersion jsonapi = new APIVersion();
+	private final APIVersion jsonapi = new APIVersion();
 
 	public ResponseWrapper() {}
 
@@ -23,6 +22,21 @@ public class ResponseWrapper<T> implements Links {
 		addError(error);
 	}
 
+	public Map<String, String> getLinks() {
+		return links;
+	}
+
+	public void setLinks(Map<String, String> links) {
+		this.links = links;
+	}
+
+	public void addLink(String name, String link) {
+		if (links == null) {
+			links = new HashMap<>();
+		}
+		links.put(name, link);
+	}
+
 	public T getData() {
 		return data;
 	}
@@ -41,14 +55,6 @@ public class ResponseWrapper<T> implements Links {
 		this.data = null;
 	}
 
-	public APIVersion getJsonapi() {
-		return jsonapi;
-	}
-
-	public void setJsonapi(APIVersion jsonapi) {
-		this.jsonapi = jsonapi;
-	}
-
 	public void addError(APIError error) {
 		if (error == null) {
 			errors = new ArrayList<>();
@@ -56,27 +62,8 @@ public class ResponseWrapper<T> implements Links {
 		errors.add(error);
 	}
 
-	@Override
-	public Map<String, Link> getLinks() {
-		return links;
-	}
-
-	@Override
-	public void setLinks(Map<String, Link> links) {
-		this.links = links;
-	}
-
-	@Override
-	public void setSelf(URI base) {
-		addLink("self", new Link(base.toString()));
-	}
-
-	@Override
-	public void addLink(String name, Link link) {
-		if (links == null) {
-			links = new HashMap<>();
-		}
-		links.put(name, link);
+	public APIVersion getJsonapi() {
+		return jsonapi;
 	}
 
 }
diff --git a/vipra-rest/src/main/java/de/vipra/rest/provider/APIRequestFilter.java b/vipra-rest/src/main/java/de/vipra/rest/provider/APIRequestFilter.java
index 85bdd37b2bf94d1ef072247dc43f13743913fffe..2a64e72df854428f7d64cca7ec6af3531c863748 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/provider/APIRequestFilter.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/provider/APIRequestFilter.java
@@ -2,42 +2,39 @@ package de.vipra.rest.provider;
 
 import java.io.IOException;
 
-import javax.ws.rs.client.ClientRequestContext;
-import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.ext.Provider;
 
 import de.vipra.rest.APIMediaType;
 
-/**
- * Request filter based on JSON API v1.0 server responsibilities
- * 
- * @see <a href="http://jsonapi.org/format/1.0/">http://jsonapi.org/format/1.0/
- *      </a>
- */
 @Provider
-public class APIRequestFilter implements ClientRequestFilter {
+public class APIRequestFilter implements ContainerRequestFilter {
 
 	@Override
-	public void filter(ClientRequestContext requestContext) throws IOException {
-		if (requestContext.getHeaderString("Content-Type").contains(";")) {
-			requestContext.abortWith(Response.status(Response.Status.UNSUPPORTED_MEDIA_TYPE).build());
-		} else {
-			String[] acceptHeader = requestContext.getHeaderString("Accept").split(",");
-			boolean abort = false;
-			for (int i = 0; i < acceptHeader.length; i++) {
-				String accept = acceptHeader[i].trim();
-				if (accept.startsWith(APIMediaType.APPLICATION_JSONAPI)) {
-					if (accept.contains(";")) {
-						abort = true;
-					} else {
-						abort = false;
-						break;
+	public void filter(ContainerRequestContext requestContext) throws IOException {
+		String contentType = requestContext.getHeaderString("Content-Type");
+		if (contentType != null) {
+			if (requestContext.getHeaderString("Content-Type").contains(";")) {
+				requestContext.abortWith(Response.status(Response.Status.UNSUPPORTED_MEDIA_TYPE).build());
+			} else {
+				String[] acceptHeader = requestContext.getHeaderString("Accept").split(",");
+				boolean abort = false;
+				for (int i = 0; i < acceptHeader.length; i++) {
+					String accept = acceptHeader[i].trim();
+					if (accept.startsWith(APIMediaType.APPLICATION_JSONAPI)) {
+						if (accept.contains(";")) {
+							abort = true;
+						} else {
+							abort = false;
+							break;
+						}
 					}
 				}
-			}
-			if (abort) {
-				requestContext.abortWith(Response.status(Response.Status.NOT_ACCEPTABLE).build());
+				if (abort) {
+					requestContext.abortWith(Response.status(Response.Status.NOT_ACCEPTABLE).build());
+				}
 			}
 		}
 	}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/provider/CORSResponseFilter.java b/vipra-rest/src/main/java/de/vipra/rest/provider/CORSResponseFilter.java
index 2cd438181c439626d45d3db53e68e2a1d5d18273..f1a79f215b70121112176002edb25eb24c9274b3 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/provider/CORSResponseFilter.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/provider/CORSResponseFilter.java
@@ -7,9 +7,18 @@ import javax.ws.rs.container.ContainerResponseContext;
 import javax.ws.rs.container.ContainerResponseFilter;
 import javax.ws.rs.ext.Provider;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 @Provider
 public class CORSResponseFilter implements ContainerResponseFilter {
 
+	public static final Logger log = LoggerFactory.getLogger(CORSResponseFilter.class);
+
+	public CORSResponseFilter() {
+		log.info("cors filter registered");
+	}
+
 	@Override
 	public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
 		response.getHeaders().add("Access-Control-Allow-Origin", "*");
diff --git a/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java b/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java
index 624b6065be81d4e631967d2df95cded40d41c82d..b818a5f9d115f8ca5e9ea65115d6c2f5f6ba9a3c 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java
@@ -3,23 +3,27 @@ package de.vipra.rest.provider;
 import javax.ws.rs.ext.ContextResolver;
 import javax.ws.rs.ext.Provider;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 
-import de.vipra.rest.model.Articles;
-import de.vipra.rest.model.Link;
-import de.vipra.rest.serializer.ArticlesSerializer;
-import de.vipra.rest.serializer.LinkSerializer;
+import de.vipra.rest.model.Article;
+import de.vipra.rest.serializer.ArticleDeserializer;
+import de.vipra.rest.serializer.ArticleSerializer;
 
 @Provider
 public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
 
+	public static final Logger log = LoggerFactory.getLogger(ObjectMapperProvider.class);
 	final ObjectMapper defaultObjectMapper;
 
 	public ObjectMapperProvider() {
 		defaultObjectMapper = createDefaultMapper();
+		log.info("object mapper provider registered");
 	}
 
 	@Override
@@ -29,8 +33,8 @@ public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
 
 	public static ObjectMapper createDefaultMapper() {
 		SimpleModule module = new SimpleModule();
-		module.addSerializer(Articles.class, new ArticlesSerializer());
-		module.addSerializer(Link.class, new LinkSerializer());
+		module.addSerializer(Article.class, new ArticleSerializer());
+		module.addDeserializer(Article.class, new ArticleDeserializer());
 
 		final ObjectMapper mapper = new ObjectMapper();
 		mapper.enable(SerializationFeature.INDENT_OUTPUT);
diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/ArticlesResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java
similarity index 73%
rename from vipra-rest/src/main/java/de/vipra/rest/resource/ArticlesResource.java
rename to vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java
index 5a25706ef800d0ac1b2ad5d4683252adf4959a19..605a80ea488ed8988e6e6fd471db69ae0cbaabf4 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/resource/ArticlesResource.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java
@@ -1,5 +1,6 @@
 package de.vipra.rest.resource;
 
+import java.io.InputStream;
 import java.util.ArrayList;
 
 import javax.servlet.ServletContext;
@@ -22,22 +23,23 @@ import de.vipra.rest.Configuration;
 import de.vipra.rest.Messages;
 import de.vipra.rest.MongoDB;
 import de.vipra.rest.model.APIError;
-import de.vipra.rest.model.Articles;
+import de.vipra.rest.model.Article;
 import de.vipra.rest.model.ResponseWrapper;
-import de.vipra.rest.service.ArticlesService;
+import de.vipra.rest.service.ArticleService;
 
 @Path("articles")
-public class ArticlesResource {
+public class ArticleResource {
 
 	@Context
 	UriInfo uri;
 
-	final ArticlesService service;
+	final ArticleService service;
 
-	public ArticlesResource(@Context ServletContext servletContext) {
-		Configuration config = new Configuration(servletContext.getResourceAsStream("config.properties"));
+	public ArticleResource(@Context ServletContext servletContext) {
+		InputStream is = servletContext.getResourceAsStream("config.properties");
+		Configuration config = new Configuration(is);
 		MongoDB mongo = MongoDB.getInstance(config);
-		service = new ArticlesService(mongo.getDatabase());
+		service = new ArticleService(mongo.getDatabase());
 	}
 
 	@GET
@@ -45,9 +47,9 @@ public class ArticlesResource {
 	public Response getArticles(@QueryParam("skip") @DefaultValue("0") int skip,
 			@QueryParam("limit") @DefaultValue("0") int limit,
 			@QueryParam("sort") @DefaultValue("date") String sortBy) {
-		ArrayList<Articles> articles = service.getArticles(uri.getAbsolutePath(), skip, limit, sortBy);
-		ResponseWrapper<ArrayList<Articles>> res = new ResponseWrapper<>(articles);
-		res.setSelf(uri.getAbsolutePath());
+		ArrayList<Article> articles = service.getArticles(uri.getAbsolutePath(), skip, limit, sortBy);
+		ResponseWrapper<ArrayList<Article>> res = new ResponseWrapper<>(articles);
+		res.addLink("self", uri.getAbsolutePath().toString());
 		return Response.ok().entity(res).build();
 	}
 
@@ -56,14 +58,13 @@ public class ArticlesResource {
 	@Consumes(APIMediaType.APPLICATION_JSONAPI)
 	@Path("{id}")
 	public Response getArticle(@PathParam("id") String id) {
-		ResponseWrapper<Articles> res = new ResponseWrapper<>();
-		res.setSelf(Articles.getURI(uri.getAbsolutePath(), id));
+		ResponseWrapper<Article> res = new ResponseWrapper<>();
 		if (id == null || id.trim().length() == 0) {
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "ID is empty",
 					String.format(Messages.BAD_REQUEST, "id cannot be empty")));
 			return Response.status(Response.Status.BAD_REQUEST).entity(res).build();
 		}
-		Articles article = service.getArticle(uri.getAbsolutePath(), id);
+		Article article = service.getArticle(uri.getAbsolutePath(), id);
 		if (article != null) {
 			res.setData(article);
 			return Response.ok().entity(res).build();
@@ -77,10 +78,9 @@ public class ArticlesResource {
 	@POST
 	@Consumes(APIMediaType.APPLICATION_JSONAPI)
 	@Produces(APIMediaType.APPLICATION_JSONAPI)
-	public Response createArticle(Articles article) {
+	public Response createArticle(Article article) {
 		article = service.createArticle(uri.getAbsolutePath(), article);
-		ResponseWrapper<Articles> res = new ResponseWrapper<>(article);
-		res.setSelf(article.getURI(uri.getAbsolutePath()));
+		ResponseWrapper<Article> res = new ResponseWrapper<>(article);
 		return Response.created(article.getURI(uri.getAbsolutePath())).entity(res).build();
 	}
 
@@ -90,10 +90,9 @@ public class ArticlesResource {
 		long deleted = service.deleteArticle(id);
 		switch (Math.toIntExact(deleted)) {
 		case 0:
-			ResponseWrapper<Articles> res = new ResponseWrapper<>();
+			ResponseWrapper<Article> res = new ResponseWrapper<>();
 			res.addError(new APIError(Response.Status.NOT_FOUND, "Article not found",
 					String.format(Messages.NOT_FOUND, "article", id)));
-			res.setSelf(Articles.getURI(uri.getAbsolutePath(), id));
 			return Response.status(Response.Status.NOT_FOUND).entity(res).build();
 		case 1:
 			return Response.noContent().build();
@@ -106,10 +105,9 @@ public class ArticlesResource {
 	@Consumes(APIMediaType.APPLICATION_JSONAPI)
 	@Produces(APIMediaType.APPLICATION_JSONAPI)
 	@Path("{id}")
-	public Response updateArticle(@PathParam("id") String id, Articles article) {
+	public Response updateArticle(@PathParam("id") String id, Article article) {
 		long updated = service.updateArticle(uri.getAbsolutePath(), article);
-		ResponseWrapper<Articles> res = new ResponseWrapper<>();
-		res.setSelf(article.getURI(uri.getAbsolutePath()));
+		ResponseWrapper<Article> res = new ResponseWrapper<>();
 		switch (Math.toIntExact(updated)) {
 		case 0:
 			res.addError(new APIError(Response.Status.NOT_FOUND, "Article not found",
diff --git a/vipra-rest/src/main/java/de/vipra/rest/serializer/ArticleDeserializer.java b/vipra-rest/src/main/java/de/vipra/rest/serializer/ArticleDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..54152e0b54e4209de180e26bd008a4ad9d713d70
--- /dev/null
+++ b/vipra-rest/src/main/java/de/vipra/rest/serializer/ArticleDeserializer.java
@@ -0,0 +1,43 @@
+package de.vipra.rest.serializer;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.vipra.rest.model.Article;
+
+import static de.vipra.rest.serializer.Helper.*;
+
+public class ArticleDeserializer extends JsonDeserializer<Article> {
+
+	@Override
+	public Article deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+		Article article = null;
+
+		JsonNode node = p.readValueAsTree();
+		if (node != null) {
+			article = new Article();
+			if (node.has("id"))
+				article.setId(getString(node, "id"));
+
+			if (node.has("attributes")) {
+				JsonNode attrs = node.get("attributes");
+				if (attrs.has("title"))
+					article.setTitle(getString(attrs, "title"));
+				if (attrs.has("text"))
+					article.setText(getString(attrs, "text"));
+				if (attrs.has("url"))
+					article.setUrl(getString(attrs, "url"));
+				if (attrs.has("date"))
+					article.setDate(stringToDate(getString(attrs, "date")));
+			}
+		}
+
+		return article;
+	}
+
+}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/serializer/ArticlesSerializer.java b/vipra-rest/src/main/java/de/vipra/rest/serializer/ArticleSerializer.java
similarity index 75%
rename from vipra-rest/src/main/java/de/vipra/rest/serializer/ArticlesSerializer.java
rename to vipra-rest/src/main/java/de/vipra/rest/serializer/ArticleSerializer.java
index b7e2c9f72e48f2ae0128c66af5da59d4312bca35..b5ce97f963540a3da36c73ce26215290d696d2fd 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/serializer/ArticlesSerializer.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/serializer/ArticleSerializer.java
@@ -1,6 +1,6 @@
 package de.vipra.rest.serializer;
 
-import de.vipra.rest.model.Articles;
+import de.vipra.rest.model.Article;
 
 import java.io.IOException;
 
@@ -9,14 +9,20 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.SerializerProvider;
 
-public class ArticlesSerializer extends JsonSerializer<Articles> {
+import static de.vipra.rest.serializer.Helper.*;
+
+public class ArticleSerializer extends JsonSerializer<Article> {
 
 	@Override
-	public void serialize(Articles value, JsonGenerator gen, SerializerProvider serializers)
+	public void serialize(Article value, JsonGenerator gen, SerializerProvider serializers)
 			throws IOException, JsonProcessingException {
 		gen.writeStartObject();
 		gen.writeStringField("id", value.getId());
 		gen.writeStringField("type", value.getType());
+
+		if (value.getLinks() != null)
+			gen.writeObjectField("links", value.getLinks());
+
 		gen.writeObjectFieldStart("attributes");
 		if (value.getTitle() != null)
 			gen.writeStringField("title", value.getTitle());
@@ -25,10 +31,9 @@ public class ArticlesSerializer extends JsonSerializer<Articles> {
 		if (value.getUrl() != null)
 			gen.writeStringField("url", value.getUrl());
 		if (value.getDate() != null)
-			gen.writeStringField("date", value.getDate().toString());
+			gen.writeStringField("date", dateToString(value.getDate()));
 		gen.writeEndObject();
-		if (value.getLinks() != null)
-			gen.writeObjectField("links", value.getLinks());
+
 		gen.writeEndObject();
 	}
 
diff --git a/vipra-rest/src/main/java/de/vipra/rest/serializer/Helper.java b/vipra-rest/src/main/java/de/vipra/rest/serializer/Helper.java
new file mode 100644
index 0000000000000000000000000000000000000000..09dd2d5ded3efa1c0b847c71628408c2edd898c4
--- /dev/null
+++ b/vipra-rest/src/main/java/de/vipra/rest/serializer/Helper.java
@@ -0,0 +1,61 @@
+package de.vipra.rest.serializer;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+public class Helper {
+
+	public static <T> T get(JsonNode node, String name, T defaultValue, Class<T> type) {
+		if (node == null) {
+			return defaultValue;
+		}
+		node = node.get(name);
+		if (node == null) {
+			return defaultValue;
+		}
+		switch (type.getSimpleName()) {
+		case "String":
+			return type.cast(node.asText());
+		case "Integer":
+			return type.cast(node.asInt());
+		case "Long":
+			return type.cast(node.asLong());
+		}
+		return null;
+	}
+
+	public static String getString(JsonNode node, String name, String defaultValue) {
+		return get(node, name, defaultValue, String.class);
+	}
+
+	public static String getString(JsonNode node, String name) {
+		return getString(node, name, null);
+	}
+
+	public static long getLong(JsonNode node, String name, long defaultValue) {
+		return get(node, name, defaultValue, Long.class);
+	}
+
+	public static long getLong(JsonNode node, String name) {
+		return getLong(node, name, 0L);
+	}
+
+	public static String dateToString(Date date) {
+		DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+		return df.format(date);
+	}
+
+	public static Date stringToDate(String source) {
+		DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+		try {
+			return df.parse(source);
+		} catch (ParseException e) {
+			return null;
+		}
+	}
+
+}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/serializer/LinkSerializer.java b/vipra-rest/src/main/java/de/vipra/rest/serializer/LinkSerializer.java
deleted file mode 100644
index f726f6743459f1c67a500a8342968c91f375e20a..0000000000000000000000000000000000000000
--- a/vipra-rest/src/main/java/de/vipra/rest/serializer/LinkSerializer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package de.vipra.rest.serializer;
-
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-
-import de.vipra.rest.model.Link;
-
-public class LinkSerializer extends JsonSerializer<Link> {
-
-	@Override
-	public void serialize(Link value, JsonGenerator gen, SerializerProvider serializers)
-			throws IOException, JsonProcessingException {
-		if (value.getMeta() != null) {
-			gen.writeStartObject();
-			gen.writeStringField("href", value.getHref());
-			gen.writeObjectField("meta", value.getMeta());
-			gen.writeEndObject();
-		} else {
-			gen.writeString(value.getHref());
-		}
-	}
-
-}
diff --git a/vipra-rest/src/main/java/de/vipra/rest/service/ArticlesService.java b/vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java
similarity index 70%
rename from vipra-rest/src/main/java/de/vipra/rest/service/ArticlesService.java
rename to vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java
index deb4bbd6c6f704ba6e1f6adaacbc3839ff38284e..4d98db3203c54822a9ca1ad887ffd47754c79bb8 100644
--- a/vipra-rest/src/main/java/de/vipra/rest/service/ArticlesService.java
+++ b/vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java
@@ -11,22 +11,22 @@ import com.mongodb.client.model.Filters;
 import com.mongodb.client.result.DeleteResult;
 import com.mongodb.client.result.UpdateResult;
 
-import de.vipra.rest.model.Articles;
+import de.vipra.rest.model.Article;
 
 import static de.vipra.rest.resource.Helper.*;
 
-public class ArticlesService {
+public class ArticleService {
 
 	final MongoCollection<Document> articles;
 
-	public ArticlesService(MongoDatabase db) {
+	public ArticleService(MongoDatabase db) {
 		articles = db.getCollection("articles");
 	}
 
-	public Articles getArticle(URI base, String id) {
+	public Article getArticle(URI base, String id) {
 		ArrayList<Document> result = articles.find(Filters.eq("_id", objectId(id))).into(new ArrayList<Document>());
 		if (result.size() == 1) {
-			Articles article = new Articles(result.get(0));
+			Article article = new Article(result.get(0));
 			article.setSelf(base);
 			return article;
 		} else {
@@ -34,22 +34,22 @@ public class ArticlesService {
 		}
 	}
 
-	public ArrayList<Articles> getArticles(URI base, int skip, int limit, String sortBy) {
+	public ArrayList<Article> getArticles(URI base, int skip, int limit, String sortBy) {
 
 		ArrayList<Document> docs = articles.find().skip(skip).limit(limit).sort(getSorts(sortBy))
 				.into(new ArrayList<Document>());
-		ArrayList<Articles> result = Articles.fromDocuments(docs);
-		for (Articles article : result) {
+		ArrayList<Article> result = Article.fromDocuments(docs);
+		for (Article article : result) {
 			article.setText(null);
 			article.setSelf(base);
 		}
 		return result;
 	}
 
-	public Articles createArticle(URI base, Articles article) {
+	public Article createArticle(URI base, Article article) {
 		Document doc = new Document(article.toDocument());
 		articles.insertOne(doc);
-		article = new Articles(doc);
+		article = new Article(doc);
 		article.setSelf(base);
 		return article;
 	}
@@ -59,10 +59,12 @@ public class ArticlesService {
 		return result.getDeletedCount();
 	}
 
-	public long updateArticle(URI base, Articles article) {
+	public long updateArticle(URI base, Article article) {
 		Document docOld = new Document("_id", objectId(article.getId()));
 		Document docNew = article.toDocument();
 		UpdateResult result = articles.replaceOne(docOld, docNew);
+		article.fromDocument(docNew);
+		article.setSelf(base);
 		return result.getModifiedCount();
 	}
 
diff --git a/vipra-rest/src/test/java/de/vipra/rest/resource/ArticleResourceTest.java b/vipra-rest/src/test/java/de/vipra/rest/resource/ArticleResourceTest.java
index c9b2d40c9acd82c7f5f4a568903143c3c2e8047b..7ef0949f4b49cbaa15e53a9b0c6cb74711b9f2a5 100644
--- a/vipra-rest/src/test/java/de/vipra/rest/resource/ArticleResourceTest.java
+++ b/vipra-rest/src/test/java/de/vipra/rest/resource/ArticleResourceTest.java
@@ -9,13 +9,13 @@ import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
 import org.junit.Test;
 
-import de.vipra.rest.resource.ArticlesResource;
+import de.vipra.rest.resource.ArticleResource;
 
 public class ArticleResourceTest extends JerseyTest {
 
 	@Override
 	protected Application configure() {
-		return new ResourceConfig(ArticlesResource.class);
+		return new ResourceConfig(ArticleResource.class);
 	}
 
 	@Test