diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000000000000000000000000000000000..d54ff1a8f7e54e52dbfe9470272b76c356856371
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,1165 @@
+[*]
+charset = utf-8
+end_of_line = crlf
+indent_size = 4
+indent_style = space
+insert_final_newline = false
+max_line_length = 120
+tab_width = 4
+ij_continuation_indent_size = 8
+ij_formatter_off_tag = @formatter:off
+ij_formatter_on_tag = @formatter:on
+ij_formatter_tags_enabled = false
+ij_smart_tabs = false
+ij_visual_guides = none
+ij_wrap_on_typing = false
+
+[*.css]
+ij_css_align_closing_brace_with_properties = false
+ij_css_blank_lines_around_nested_selector = 1
+ij_css_blank_lines_between_blocks = 1
+ij_css_block_comment_add_space = false
+ij_css_brace_placement = end_of_line
+ij_css_enforce_quotes_on_format = false
+ij_css_hex_color_long_format = false
+ij_css_hex_color_lower_case = false
+ij_css_hex_color_short_format = false
+ij_css_hex_color_upper_case = false
+ij_css_keep_blank_lines_in_code = 2
+ij_css_keep_indents_on_empty_lines = false
+ij_css_keep_single_line_blocks = false
+ij_css_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
+ij_css_space_after_colon = true
+ij_css_space_before_opening_brace = true
+ij_css_use_double_quotes = true
+ij_css_value_alignment = do_not_align
+
+[*.feature]
+indent_size = 2
+ij_gherkin_keep_indents_on_empty_lines = false
+
+[*.haml]
+indent_size = 2
+ij_haml_keep_indents_on_empty_lines = false
+
+[*.java]
+ij_java_align_consecutive_assignments = false
+ij_java_align_consecutive_variable_declarations = false
+ij_java_align_group_field_declarations = false
+ij_java_align_multiline_annotation_parameters = false
+ij_java_align_multiline_array_initializer_expression = false
+ij_java_align_multiline_assignment = false
+ij_java_align_multiline_binary_operation = false
+ij_java_align_multiline_chained_methods = false
+ij_java_align_multiline_extends_list = false
+ij_java_align_multiline_for = true
+ij_java_align_multiline_method_parentheses = false
+ij_java_align_multiline_parameters = true
+ij_java_align_multiline_parameters_in_calls = false
+ij_java_align_multiline_parenthesized_expression = false
+ij_java_align_multiline_records = true
+ij_java_align_multiline_resources = true
+ij_java_align_multiline_ternary_operation = false
+ij_java_align_multiline_text_blocks = false
+ij_java_align_multiline_throws_list = false
+ij_java_align_subsequent_simple_methods = false
+ij_java_align_throws_keyword = false
+ij_java_align_types_in_multi_catch = true
+ij_java_annotation_parameter_wrap = off
+ij_java_array_initializer_new_line_after_left_brace = false
+ij_java_array_initializer_right_brace_on_new_line = false
+ij_java_array_initializer_wrap = off
+ij_java_assert_statement_colon_on_next_line = false
+ij_java_assert_statement_wrap = off
+ij_java_assignment_wrap = off
+ij_java_binary_operation_sign_on_next_line = false
+ij_java_binary_operation_wrap = off
+ij_java_blank_lines_after_anonymous_class_header = 0
+ij_java_blank_lines_after_class_header = 0
+ij_java_blank_lines_after_imports = 1
+ij_java_blank_lines_after_package = 1
+ij_java_blank_lines_around_class = 1
+ij_java_blank_lines_around_field = 0
+ij_java_blank_lines_around_field_in_interface = 0
+ij_java_blank_lines_around_initializer = 1
+ij_java_blank_lines_around_method = 1
+ij_java_blank_lines_around_method_in_interface = 1
+ij_java_blank_lines_before_class_end = 0
+ij_java_blank_lines_before_imports = 1
+ij_java_blank_lines_before_method_body = 0
+ij_java_blank_lines_before_package = 0
+ij_java_block_brace_style = end_of_line
+ij_java_block_comment_add_space = false
+ij_java_block_comment_at_first_column = true
+ij_java_builder_methods = none
+ij_java_call_parameters_new_line_after_left_paren = false
+ij_java_call_parameters_right_paren_on_new_line = false
+ij_java_call_parameters_wrap = off
+ij_java_case_statement_on_separate_line = true
+ij_java_catch_on_new_line = false
+ij_java_class_annotation_wrap = split_into_lines
+ij_java_class_brace_style = end_of_line
+ij_java_class_count_to_use_import_on_demand = 5
+ij_java_class_names_in_javadoc = 1
+ij_java_do_not_indent_top_level_class_members = false
+ij_java_do_not_wrap_after_single_annotation = false
+ij_java_do_not_wrap_after_single_annotation_in_parameter = false
+ij_java_do_while_brace_force = never
+ij_java_doc_add_blank_line_after_description = true
+ij_java_doc_add_blank_line_after_param_comments = false
+ij_java_doc_add_blank_line_after_return = false
+ij_java_doc_add_p_tag_on_empty_lines = true
+ij_java_doc_align_exception_comments = true
+ij_java_doc_align_param_comments = true
+ij_java_doc_do_not_wrap_if_one_line = false
+ij_java_doc_enable_formatting = true
+ij_java_doc_enable_leading_asterisks = true
+ij_java_doc_indent_on_continuation = false
+ij_java_doc_keep_empty_lines = true
+ij_java_doc_keep_empty_parameter_tag = true
+ij_java_doc_keep_empty_return_tag = true
+ij_java_doc_keep_empty_throws_tag = true
+ij_java_doc_keep_invalid_tags = true
+ij_java_doc_param_description_on_new_line = false
+ij_java_doc_preserve_line_breaks = false
+ij_java_doc_use_throws_not_exception_tag = true
+ij_java_else_on_new_line = false
+ij_java_entity_dd_suffix = EJB
+ij_java_entity_eb_suffix = Bean
+ij_java_entity_hi_suffix = Home
+ij_java_entity_lhi_prefix = Local
+ij_java_entity_lhi_suffix = Home
+ij_java_entity_li_prefix = Local
+ij_java_entity_pk_class = java.lang.String
+ij_java_entity_vo_suffix = VO
+ij_java_enum_constants_wrap = off
+ij_java_extends_keyword_wrap = off
+ij_java_extends_list_wrap = off
+ij_java_field_annotation_wrap = split_into_lines
+ij_java_finally_on_new_line = false
+ij_java_for_brace_force = never
+ij_java_for_statement_new_line_after_left_paren = false
+ij_java_for_statement_right_paren_on_new_line = false
+ij_java_for_statement_wrap = off
+ij_java_generate_final_locals = false
+ij_java_generate_final_parameters = false
+ij_java_if_brace_force = never
+ij_java_imports_layout = *, |, javax.**, java.**, |, $*
+ij_java_indent_case_from_switch = true
+ij_java_insert_inner_class_imports = false
+ij_java_insert_override_annotation = true
+ij_java_keep_blank_lines_before_right_brace = 2
+ij_java_keep_blank_lines_between_package_declaration_and_header = 2
+ij_java_keep_blank_lines_in_code = 2
+ij_java_keep_blank_lines_in_declarations = 2
+ij_java_keep_builder_methods_indents = false
+ij_java_keep_control_statement_in_one_line = true
+ij_java_keep_first_column_comment = true
+ij_java_keep_indents_on_empty_lines = false
+ij_java_keep_line_breaks = true
+ij_java_keep_multiple_expressions_in_one_line = false
+ij_java_keep_simple_blocks_in_one_line = false
+ij_java_keep_simple_classes_in_one_line = false
+ij_java_keep_simple_lambdas_in_one_line = false
+ij_java_keep_simple_methods_in_one_line = false
+ij_java_label_indent_absolute = false
+ij_java_label_indent_size = 0
+ij_java_lambda_brace_style = end_of_line
+ij_java_layout_static_imports_separately = true
+ij_java_line_comment_add_space = false
+ij_java_line_comment_add_space_on_reformat = false
+ij_java_line_comment_at_first_column = true
+ij_java_message_dd_suffix = EJB
+ij_java_message_eb_suffix = Bean
+ij_java_method_annotation_wrap = split_into_lines
+ij_java_method_brace_style = end_of_line
+ij_java_method_call_chain_wrap = off
+ij_java_method_parameters_new_line_after_left_paren = false
+ij_java_method_parameters_right_paren_on_new_line = false
+ij_java_method_parameters_wrap = off
+ij_java_modifier_list_wrap = false
+ij_java_multi_catch_types_wrap = normal
+ij_java_names_count_to_use_import_on_demand = 3
+ij_java_new_line_after_lparen_in_annotation = false
+ij_java_new_line_after_lparen_in_record_header = false
+ij_java_packages_to_use_import_on_demand = java.awt.*, javax.swing.*
+ij_java_parameter_annotation_wrap = off
+ij_java_parentheses_expression_new_line_after_left_paren = false
+ij_java_parentheses_expression_right_paren_on_new_line = false
+ij_java_place_assignment_sign_on_next_line = false
+ij_java_prefer_longer_names = true
+ij_java_prefer_parameters_wrap = false
+ij_java_record_components_wrap = normal
+ij_java_repeat_synchronized = true
+ij_java_replace_instanceof_and_cast = false
+ij_java_replace_null_check = true
+ij_java_replace_sum_lambda_with_method_ref = true
+ij_java_resource_list_new_line_after_left_paren = false
+ij_java_resource_list_right_paren_on_new_line = false
+ij_java_resource_list_wrap = off
+ij_java_rparen_on_new_line_in_annotation = false
+ij_java_rparen_on_new_line_in_record_header = false
+ij_java_session_dd_suffix = EJB
+ij_java_session_eb_suffix = Bean
+ij_java_session_hi_suffix = Home
+ij_java_session_lhi_prefix = Local
+ij_java_session_lhi_suffix = Home
+ij_java_session_li_prefix = Local
+ij_java_session_si_suffix = Service
+ij_java_space_after_closing_angle_bracket_in_type_argument = false
+ij_java_space_after_colon = true
+ij_java_space_after_comma = true
+ij_java_space_after_comma_in_type_arguments = true
+ij_java_space_after_for_semicolon = true
+ij_java_space_after_quest = true
+ij_java_space_after_type_cast = true
+ij_java_space_before_annotation_array_initializer_left_brace = false
+ij_java_space_before_annotation_parameter_list = false
+ij_java_space_before_array_initializer_left_brace = false
+ij_java_space_before_catch_keyword = true
+ij_java_space_before_catch_left_brace = true
+ij_java_space_before_catch_parentheses = true
+ij_java_space_before_class_left_brace = true
+ij_java_space_before_colon = true
+ij_java_space_before_colon_in_foreach = true
+ij_java_space_before_comma = false
+ij_java_space_before_do_left_brace = true
+ij_java_space_before_else_keyword = true
+ij_java_space_before_else_left_brace = true
+ij_java_space_before_finally_keyword = true
+ij_java_space_before_finally_left_brace = true
+ij_java_space_before_for_left_brace = true
+ij_java_space_before_for_parentheses = true
+ij_java_space_before_for_semicolon = false
+ij_java_space_before_if_left_brace = true
+ij_java_space_before_if_parentheses = true
+ij_java_space_before_method_call_parentheses = false
+ij_java_space_before_method_left_brace = true
+ij_java_space_before_method_parentheses = false
+ij_java_space_before_opening_angle_bracket_in_type_parameter = false
+ij_java_space_before_quest = true
+ij_java_space_before_switch_left_brace = true
+ij_java_space_before_switch_parentheses = true
+ij_java_space_before_synchronized_left_brace = true
+ij_java_space_before_synchronized_parentheses = true
+ij_java_space_before_try_left_brace = true
+ij_java_space_before_try_parentheses = true
+ij_java_space_before_type_parameter_list = false
+ij_java_space_before_while_keyword = true
+ij_java_space_before_while_left_brace = true
+ij_java_space_before_while_parentheses = true
+ij_java_space_inside_one_line_enum_braces = false
+ij_java_space_within_empty_array_initializer_braces = false
+ij_java_space_within_empty_method_call_parentheses = false
+ij_java_space_within_empty_method_parentheses = false
+ij_java_spaces_around_additive_operators = true
+ij_java_spaces_around_annotation_eq = true
+ij_java_spaces_around_assignment_operators = true
+ij_java_spaces_around_bitwise_operators = true
+ij_java_spaces_around_equality_operators = true
+ij_java_spaces_around_lambda_arrow = true
+ij_java_spaces_around_logical_operators = true
+ij_java_spaces_around_method_ref_dbl_colon = false
+ij_java_spaces_around_multiplicative_operators = true
+ij_java_spaces_around_relational_operators = true
+ij_java_spaces_around_shift_operators = true
+ij_java_spaces_around_type_bounds_in_type_parameters = true
+ij_java_spaces_around_unary_operator = false
+ij_java_spaces_within_angle_brackets = false
+ij_java_spaces_within_annotation_parentheses = false
+ij_java_spaces_within_array_initializer_braces = false
+ij_java_spaces_within_braces = false
+ij_java_spaces_within_brackets = false
+ij_java_spaces_within_cast_parentheses = false
+ij_java_spaces_within_catch_parentheses = false
+ij_java_spaces_within_for_parentheses = false
+ij_java_spaces_within_if_parentheses = false
+ij_java_spaces_within_method_call_parentheses = false
+ij_java_spaces_within_method_parentheses = false
+ij_java_spaces_within_parentheses = false
+ij_java_spaces_within_record_header = false
+ij_java_spaces_within_switch_parentheses = false
+ij_java_spaces_within_synchronized_parentheses = false
+ij_java_spaces_within_try_parentheses = false
+ij_java_spaces_within_while_parentheses = false
+ij_java_special_else_if_treatment = true
+ij_java_subclass_name_suffix = Impl
+ij_java_ternary_operation_signs_on_next_line = false
+ij_java_ternary_operation_wrap = off
+ij_java_test_name_suffix = Test
+ij_java_throws_keyword_wrap = off
+ij_java_throws_list_wrap = off
+ij_java_use_external_annotations = false
+ij_java_use_fq_class_names = false
+ij_java_use_relative_indents = false
+ij_java_use_single_class_imports = true
+ij_java_variable_annotation_wrap = off
+ij_java_visibility = public
+ij_java_while_brace_force = never
+ij_java_while_on_new_line = false
+ij_java_wrap_comments = false
+ij_java_wrap_first_method_in_call_chain = false
+ij_java_wrap_long_lines = false
+
+[*.less]
+indent_size = 2
+ij_less_align_closing_brace_with_properties = false
+ij_less_blank_lines_around_nested_selector = 1
+ij_less_blank_lines_between_blocks = 1
+ij_less_block_comment_add_space = false
+ij_less_brace_placement = 0
+ij_less_enforce_quotes_on_format = false
+ij_less_hex_color_long_format = false
+ij_less_hex_color_lower_case = false
+ij_less_hex_color_short_format = false
+ij_less_hex_color_upper_case = false
+ij_less_keep_blank_lines_in_code = 2
+ij_less_keep_indents_on_empty_lines = false
+ij_less_keep_single_line_blocks = false
+ij_less_line_comment_add_space = false
+ij_less_line_comment_at_first_column = false
+ij_less_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
+ij_less_space_after_colon = true
+ij_less_space_before_opening_brace = true
+ij_less_use_double_quotes = true
+ij_less_value_alignment = 0
+
+[*.proto]
+indent_size = 2
+tab_width = 2
+ij_continuation_indent_size = 4
+ij_protobuf_keep_blank_lines_in_code = 2
+ij_protobuf_keep_indents_on_empty_lines = false
+ij_protobuf_keep_line_breaks = true
+ij_protobuf_space_after_comma = true
+ij_protobuf_space_before_comma = false
+ij_protobuf_spaces_around_assignment_operators = true
+ij_protobuf_spaces_within_braces = false
+ij_protobuf_spaces_within_brackets = false
+
+[*.sass]
+indent_size = 2
+ij_sass_align_closing_brace_with_properties = false
+ij_sass_blank_lines_around_nested_selector = 1
+ij_sass_blank_lines_between_blocks = 1
+ij_sass_brace_placement = 0
+ij_sass_enforce_quotes_on_format = false
+ij_sass_hex_color_long_format = false
+ij_sass_hex_color_lower_case = false
+ij_sass_hex_color_short_format = false
+ij_sass_hex_color_upper_case = false
+ij_sass_keep_blank_lines_in_code = 2
+ij_sass_keep_indents_on_empty_lines = false
+ij_sass_keep_single_line_blocks = false
+ij_sass_line_comment_add_space = false
+ij_sass_line_comment_at_first_column = false
+ij_sass_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
+ij_sass_space_after_colon = true
+ij_sass_space_before_opening_brace = true
+ij_sass_use_double_quotes = true
+ij_sass_value_alignment = 0
+
+[*.scss]
+indent_size = 2
+ij_scss_align_closing_brace_with_properties = false
+ij_scss_blank_lines_around_nested_selector = 1
+ij_scss_blank_lines_between_blocks = 1
+ij_scss_block_comment_add_space = false
+ij_scss_brace_placement = 0
+ij_scss_enforce_quotes_on_format = false
+ij_scss_hex_color_long_format = false
+ij_scss_hex_color_lower_case = false
+ij_scss_hex_color_short_format = false
+ij_scss_hex_color_upper_case = false
+ij_scss_keep_blank_lines_in_code = 2
+ij_scss_keep_indents_on_empty_lines = false
+ij_scss_keep_single_line_blocks = false
+ij_scss_line_comment_add_space = false
+ij_scss_line_comment_at_first_column = false
+ij_scss_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
+ij_scss_space_after_colon = true
+ij_scss_space_before_opening_brace = true
+ij_scss_use_double_quotes = true
+ij_scss_value_alignment = 0
+
+[*.styl]
+indent_size = 2
+ij_stylus_align_closing_brace_with_properties = false
+ij_stylus_blank_lines_around_nested_selector = 1
+ij_stylus_blank_lines_between_blocks = 1
+ij_stylus_brace_placement = 0
+ij_stylus_enforce_quotes_on_format = false
+ij_stylus_hex_color_long_format = false
+ij_stylus_hex_color_lower_case = false
+ij_stylus_hex_color_short_format = false
+ij_stylus_hex_color_upper_case = false
+ij_stylus_keep_blank_lines_in_code = 2
+ij_stylus_keep_indents_on_empty_lines = false
+ij_stylus_keep_single_line_blocks = false
+ij_stylus_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
+ij_stylus_space_after_colon = true
+ij_stylus_space_before_opening_brace = true
+ij_stylus_use_double_quotes = true
+ij_stylus_value_alignment = 0
+
+[.editorconfig]
+ij_editorconfig_align_group_field_declarations = false
+ij_editorconfig_space_after_colon = false
+ij_editorconfig_space_after_comma = true
+ij_editorconfig_space_before_colon = false
+ij_editorconfig_space_before_comma = false
+ij_editorconfig_spaces_around_assignment_operators = true
+
+[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
+ij_xml_align_attributes = true
+ij_xml_align_text = false
+ij_xml_attribute_wrap = normal
+ij_xml_block_comment_add_space = false
+ij_xml_block_comment_at_first_column = true
+ij_xml_keep_blank_lines = 2
+ij_xml_keep_indents_on_empty_lines = false
+ij_xml_keep_line_breaks = true
+ij_xml_keep_line_breaks_in_text = true
+ij_xml_keep_whitespaces = false
+ij_xml_keep_whitespaces_around_cdata = preserve
+ij_xml_keep_whitespaces_inside_cdata = false
+ij_xml_line_comment_at_first_column = true
+ij_xml_space_after_tag_name = false
+ij_xml_space_around_equals_in_attribute = false
+ij_xml_space_inside_empty_tag = false
+ij_xml_text_wrap = normal
+ij_xml_use_custom_settings = false
+
+[{*.ats,*.cts,*.mts,*.ts}]
+ij_continuation_indent_size = 4
+ij_typescript_align_imports = false
+ij_typescript_align_multiline_array_initializer_expression = false
+ij_typescript_align_multiline_binary_operation = false
+ij_typescript_align_multiline_chained_methods = false
+ij_typescript_align_multiline_extends_list = false
+ij_typescript_align_multiline_for = true
+ij_typescript_align_multiline_parameters = true
+ij_typescript_align_multiline_parameters_in_calls = false
+ij_typescript_align_multiline_ternary_operation = false
+ij_typescript_align_object_properties = 0
+ij_typescript_align_union_types = false
+ij_typescript_align_var_statements = 0
+ij_typescript_array_initializer_new_line_after_left_brace = false
+ij_typescript_array_initializer_right_brace_on_new_line = false
+ij_typescript_array_initializer_wrap = off
+ij_typescript_assignment_wrap = off
+ij_typescript_binary_operation_sign_on_next_line = false
+ij_typescript_binary_operation_wrap = off
+ij_typescript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/**
+ij_typescript_blank_lines_after_imports = 1
+ij_typescript_blank_lines_around_class = 1
+ij_typescript_blank_lines_around_field = 0
+ij_typescript_blank_lines_around_field_in_interface = 0
+ij_typescript_blank_lines_around_function = 1
+ij_typescript_blank_lines_around_method = 1
+ij_typescript_blank_lines_around_method_in_interface = 1
+ij_typescript_block_brace_style = end_of_line
+ij_typescript_block_comment_add_space = false
+ij_typescript_block_comment_at_first_column = true
+ij_typescript_call_parameters_new_line_after_left_paren = false
+ij_typescript_call_parameters_right_paren_on_new_line = false
+ij_typescript_call_parameters_wrap = off
+ij_typescript_catch_on_new_line = false
+ij_typescript_chained_call_dot_on_new_line = true
+ij_typescript_class_brace_style = end_of_line
+ij_typescript_comma_on_new_line = false
+ij_typescript_do_while_brace_force = never
+ij_typescript_else_on_new_line = false
+ij_typescript_enforce_trailing_comma = keep
+ij_typescript_enum_constants_wrap = on_every_item
+ij_typescript_extends_keyword_wrap = off
+ij_typescript_extends_list_wrap = off
+ij_typescript_field_prefix = _
+ij_typescript_file_name_style = relaxed
+ij_typescript_finally_on_new_line = false
+ij_typescript_for_brace_force = never
+ij_typescript_for_statement_new_line_after_left_paren = false
+ij_typescript_for_statement_right_paren_on_new_line = false
+ij_typescript_for_statement_wrap = off
+ij_typescript_force_quote_style = false
+ij_typescript_force_semicolon_style = false
+ij_typescript_function_expression_brace_style = end_of_line
+ij_typescript_if_brace_force = never
+ij_typescript_import_merge_members = global
+ij_typescript_import_prefer_absolute_path = global
+ij_typescript_import_sort_members = true
+ij_typescript_import_sort_module_name = false
+ij_typescript_import_use_node_resolution = true
+ij_typescript_imports_wrap = on_every_item
+ij_typescript_indent_case_from_switch = true
+ij_typescript_indent_chained_calls = true
+ij_typescript_indent_package_children = 0
+ij_typescript_jsdoc_include_types = false
+ij_typescript_jsx_attribute_value = braces
+ij_typescript_keep_blank_lines_in_code = 2
+ij_typescript_keep_first_column_comment = true
+ij_typescript_keep_indents_on_empty_lines = false
+ij_typescript_keep_line_breaks = true
+ij_typescript_keep_simple_blocks_in_one_line = false
+ij_typescript_keep_simple_methods_in_one_line = false
+ij_typescript_line_comment_add_space = true
+ij_typescript_line_comment_at_first_column = false
+ij_typescript_method_brace_style = end_of_line
+ij_typescript_method_call_chain_wrap = off
+ij_typescript_method_parameters_new_line_after_left_paren = false
+ij_typescript_method_parameters_right_paren_on_new_line = false
+ij_typescript_method_parameters_wrap = off
+ij_typescript_object_literal_wrap = on_every_item
+ij_typescript_parentheses_expression_new_line_after_left_paren = false
+ij_typescript_parentheses_expression_right_paren_on_new_line = false
+ij_typescript_place_assignment_sign_on_next_line = false
+ij_typescript_prefer_as_type_cast = false
+ij_typescript_prefer_explicit_types_function_expression_returns = false
+ij_typescript_prefer_explicit_types_function_returns = false
+ij_typescript_prefer_explicit_types_vars_fields = false
+ij_typescript_prefer_parameters_wrap = false
+ij_typescript_reformat_c_style_comments = false
+ij_typescript_space_after_colon = true
+ij_typescript_space_after_comma = true
+ij_typescript_space_after_dots_in_rest_parameter = false
+ij_typescript_space_after_generator_mult = true
+ij_typescript_space_after_property_colon = true
+ij_typescript_space_after_quest = true
+ij_typescript_space_after_type_colon = true
+ij_typescript_space_after_unary_not = false
+ij_typescript_space_before_async_arrow_lparen = true
+ij_typescript_space_before_catch_keyword = true
+ij_typescript_space_before_catch_left_brace = true
+ij_typescript_space_before_catch_parentheses = true
+ij_typescript_space_before_class_lbrace = true
+ij_typescript_space_before_class_left_brace = true
+ij_typescript_space_before_colon = true
+ij_typescript_space_before_comma = false
+ij_typescript_space_before_do_left_brace = true
+ij_typescript_space_before_else_keyword = true
+ij_typescript_space_before_else_left_brace = true
+ij_typescript_space_before_finally_keyword = true
+ij_typescript_space_before_finally_left_brace = true
+ij_typescript_space_before_for_left_brace = true
+ij_typescript_space_before_for_parentheses = true
+ij_typescript_space_before_for_semicolon = false
+ij_typescript_space_before_function_left_parenth = true
+ij_typescript_space_before_generator_mult = false
+ij_typescript_space_before_if_left_brace = true
+ij_typescript_space_before_if_parentheses = true
+ij_typescript_space_before_method_call_parentheses = false
+ij_typescript_space_before_method_left_brace = true
+ij_typescript_space_before_method_parentheses = false
+ij_typescript_space_before_property_colon = false
+ij_typescript_space_before_quest = true
+ij_typescript_space_before_switch_left_brace = true
+ij_typescript_space_before_switch_parentheses = true
+ij_typescript_space_before_try_left_brace = true
+ij_typescript_space_before_type_colon = false
+ij_typescript_space_before_unary_not = false
+ij_typescript_space_before_while_keyword = true
+ij_typescript_space_before_while_left_brace = true
+ij_typescript_space_before_while_parentheses = true
+ij_typescript_spaces_around_additive_operators = true
+ij_typescript_spaces_around_arrow_function_operator = true
+ij_typescript_spaces_around_assignment_operators = true
+ij_typescript_spaces_around_bitwise_operators = true
+ij_typescript_spaces_around_equality_operators = true
+ij_typescript_spaces_around_logical_operators = true
+ij_typescript_spaces_around_multiplicative_operators = true
+ij_typescript_spaces_around_relational_operators = true
+ij_typescript_spaces_around_shift_operators = true
+ij_typescript_spaces_around_unary_operator = false
+ij_typescript_spaces_within_array_initializer_brackets = false
+ij_typescript_spaces_within_brackets = false
+ij_typescript_spaces_within_catch_parentheses = false
+ij_typescript_spaces_within_for_parentheses = false
+ij_typescript_spaces_within_if_parentheses = false
+ij_typescript_spaces_within_imports = false
+ij_typescript_spaces_within_interpolation_expressions = false
+ij_typescript_spaces_within_method_call_parentheses = false
+ij_typescript_spaces_within_method_parentheses = false
+ij_typescript_spaces_within_object_literal_braces = false
+ij_typescript_spaces_within_object_type_braces = true
+ij_typescript_spaces_within_parentheses = false
+ij_typescript_spaces_within_switch_parentheses = false
+ij_typescript_spaces_within_type_assertion = false
+ij_typescript_spaces_within_union_types = true
+ij_typescript_spaces_within_while_parentheses = false
+ij_typescript_special_else_if_treatment = true
+ij_typescript_ternary_operation_signs_on_next_line = false
+ij_typescript_ternary_operation_wrap = off
+ij_typescript_union_types_wrap = on_every_item
+ij_typescript_use_chained_calls_group_indents = false
+ij_typescript_use_double_quotes = true
+ij_typescript_use_explicit_js_extension = auto
+ij_typescript_use_path_mapping = always
+ij_typescript_use_public_modifier = false
+ij_typescript_use_semicolon_after_statement = true
+ij_typescript_var_declaration_wrap = normal
+ij_typescript_while_brace_force = never
+ij_typescript_while_on_new_line = false
+ij_typescript_wrap_comments = false
+
+[{*.bash,*.sh,*.zsh}]
+indent_size = 2
+tab_width = 2
+ij_shell_binary_ops_start_line = false
+ij_shell_keep_column_alignment_padding = false
+ij_shell_minify_program = false
+ij_shell_redirect_followed_by_space = false
+ij_shell_switch_cases_indented = false
+ij_shell_use_unix_line_separator = true
+
+[{*.cjs,*.js}]
+ij_continuation_indent_size = 4
+ij_javascript_align_imports = false
+ij_javascript_align_multiline_array_initializer_expression = false
+ij_javascript_align_multiline_binary_operation = false
+ij_javascript_align_multiline_chained_methods = false
+ij_javascript_align_multiline_extends_list = false
+ij_javascript_align_multiline_for = true
+ij_javascript_align_multiline_parameters = true
+ij_javascript_align_multiline_parameters_in_calls = false
+ij_javascript_align_multiline_ternary_operation = false
+ij_javascript_align_object_properties = 0
+ij_javascript_align_union_types = false
+ij_javascript_align_var_statements = 0
+ij_javascript_array_initializer_new_line_after_left_brace = false
+ij_javascript_array_initializer_right_brace_on_new_line = false
+ij_javascript_array_initializer_wrap = off
+ij_javascript_assignment_wrap = off
+ij_javascript_binary_operation_sign_on_next_line = false
+ij_javascript_binary_operation_wrap = off
+ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/**
+ij_javascript_blank_lines_after_imports = 1
+ij_javascript_blank_lines_around_class = 1
+ij_javascript_blank_lines_around_field = 0
+ij_javascript_blank_lines_around_function = 1
+ij_javascript_blank_lines_around_method = 1
+ij_javascript_block_brace_style = end_of_line
+ij_javascript_block_comment_add_space = false
+ij_javascript_block_comment_at_first_column = true
+ij_javascript_call_parameters_new_line_after_left_paren = false
+ij_javascript_call_parameters_right_paren_on_new_line = false
+ij_javascript_call_parameters_wrap = off
+ij_javascript_catch_on_new_line = false
+ij_javascript_chained_call_dot_on_new_line = true
+ij_javascript_class_brace_style = end_of_line
+ij_javascript_comma_on_new_line = false
+ij_javascript_do_while_brace_force = never
+ij_javascript_else_on_new_line = false
+ij_javascript_enforce_trailing_comma = keep
+ij_javascript_extends_keyword_wrap = off
+ij_javascript_extends_list_wrap = off
+ij_javascript_field_prefix = _
+ij_javascript_file_name_style = relaxed
+ij_javascript_finally_on_new_line = false
+ij_javascript_for_brace_force = never
+ij_javascript_for_statement_new_line_after_left_paren = false
+ij_javascript_for_statement_right_paren_on_new_line = false
+ij_javascript_for_statement_wrap = off
+ij_javascript_force_quote_style = false
+ij_javascript_force_semicolon_style = false
+ij_javascript_function_expression_brace_style = end_of_line
+ij_javascript_if_brace_force = never
+ij_javascript_import_merge_members = global
+ij_javascript_import_prefer_absolute_path = global
+ij_javascript_import_sort_members = true
+ij_javascript_import_sort_module_name = false
+ij_javascript_import_use_node_resolution = true
+ij_javascript_imports_wrap = on_every_item
+ij_javascript_indent_case_from_switch = true
+ij_javascript_indent_chained_calls = true
+ij_javascript_indent_package_children = 0
+ij_javascript_jsx_attribute_value = braces
+ij_javascript_keep_blank_lines_in_code = 2
+ij_javascript_keep_first_column_comment = true
+ij_javascript_keep_indents_on_empty_lines = false
+ij_javascript_keep_line_breaks = true
+ij_javascript_keep_simple_blocks_in_one_line = false
+ij_javascript_keep_simple_methods_in_one_line = false
+ij_javascript_line_comment_add_space = true
+ij_javascript_line_comment_at_first_column = false
+ij_javascript_method_brace_style = end_of_line
+ij_javascript_method_call_chain_wrap = off
+ij_javascript_method_parameters_new_line_after_left_paren = false
+ij_javascript_method_parameters_right_paren_on_new_line = false
+ij_javascript_method_parameters_wrap = off
+ij_javascript_object_literal_wrap = on_every_item
+ij_javascript_parentheses_expression_new_line_after_left_paren = false
+ij_javascript_parentheses_expression_right_paren_on_new_line = false
+ij_javascript_place_assignment_sign_on_next_line = false
+ij_javascript_prefer_as_type_cast = false
+ij_javascript_prefer_explicit_types_function_expression_returns = false
+ij_javascript_prefer_explicit_types_function_returns = false
+ij_javascript_prefer_explicit_types_vars_fields = false
+ij_javascript_prefer_parameters_wrap = false
+ij_javascript_reformat_c_style_comments = false
+ij_javascript_space_after_colon = true
+ij_javascript_space_after_comma = true
+ij_javascript_space_after_dots_in_rest_parameter = false
+ij_javascript_space_after_generator_mult = true
+ij_javascript_space_after_property_colon = true
+ij_javascript_space_after_quest = true
+ij_javascript_space_after_type_colon = true
+ij_javascript_space_after_unary_not = false
+ij_javascript_space_before_async_arrow_lparen = true
+ij_javascript_space_before_catch_keyword = true
+ij_javascript_space_before_catch_left_brace = true
+ij_javascript_space_before_catch_parentheses = true
+ij_javascript_space_before_class_lbrace = true
+ij_javascript_space_before_class_left_brace = true
+ij_javascript_space_before_colon = true
+ij_javascript_space_before_comma = false
+ij_javascript_space_before_do_left_brace = true
+ij_javascript_space_before_else_keyword = true
+ij_javascript_space_before_else_left_brace = true
+ij_javascript_space_before_finally_keyword = true
+ij_javascript_space_before_finally_left_brace = true
+ij_javascript_space_before_for_left_brace = true
+ij_javascript_space_before_for_parentheses = true
+ij_javascript_space_before_for_semicolon = false
+ij_javascript_space_before_function_left_parenth = true
+ij_javascript_space_before_generator_mult = false
+ij_javascript_space_before_if_left_brace = true
+ij_javascript_space_before_if_parentheses = true
+ij_javascript_space_before_method_call_parentheses = false
+ij_javascript_space_before_method_left_brace = true
+ij_javascript_space_before_method_parentheses = false
+ij_javascript_space_before_property_colon = false
+ij_javascript_space_before_quest = true
+ij_javascript_space_before_switch_left_brace = true
+ij_javascript_space_before_switch_parentheses = true
+ij_javascript_space_before_try_left_brace = true
+ij_javascript_space_before_type_colon = false
+ij_javascript_space_before_unary_not = false
+ij_javascript_space_before_while_keyword = true
+ij_javascript_space_before_while_left_brace = true
+ij_javascript_space_before_while_parentheses = true
+ij_javascript_spaces_around_additive_operators = true
+ij_javascript_spaces_around_arrow_function_operator = true
+ij_javascript_spaces_around_assignment_operators = true
+ij_javascript_spaces_around_bitwise_operators = true
+ij_javascript_spaces_around_equality_operators = true
+ij_javascript_spaces_around_logical_operators = true
+ij_javascript_spaces_around_multiplicative_operators = true
+ij_javascript_spaces_around_relational_operators = true
+ij_javascript_spaces_around_shift_operators = true
+ij_javascript_spaces_around_unary_operator = false
+ij_javascript_spaces_within_array_initializer_brackets = false
+ij_javascript_spaces_within_brackets = false
+ij_javascript_spaces_within_catch_parentheses = false
+ij_javascript_spaces_within_for_parentheses = false
+ij_javascript_spaces_within_if_parentheses = false
+ij_javascript_spaces_within_imports = false
+ij_javascript_spaces_within_interpolation_expressions = false
+ij_javascript_spaces_within_method_call_parentheses = false
+ij_javascript_spaces_within_method_parentheses = false
+ij_javascript_spaces_within_object_literal_braces = false
+ij_javascript_spaces_within_object_type_braces = true
+ij_javascript_spaces_within_parentheses = false
+ij_javascript_spaces_within_switch_parentheses = false
+ij_javascript_spaces_within_type_assertion = false
+ij_javascript_spaces_within_union_types = true
+ij_javascript_spaces_within_while_parentheses = false
+ij_javascript_special_else_if_treatment = true
+ij_javascript_ternary_operation_signs_on_next_line = false
+ij_javascript_ternary_operation_wrap = off
+ij_javascript_union_types_wrap = on_every_item
+ij_javascript_use_chained_calls_group_indents = false
+ij_javascript_use_double_quotes = true
+ij_javascript_use_explicit_js_extension = auto
+ij_javascript_use_path_mapping = always
+ij_javascript_use_public_modifier = false
+ij_javascript_use_semicolon_after_statement = true
+ij_javascript_var_declaration_wrap = normal
+ij_javascript_while_brace_force = never
+ij_javascript_while_on_new_line = false
+ij_javascript_wrap_comments = false
+
+[{*.ft,*.vm,*.vsl}]
+ij_vtl_keep_indents_on_empty_lines = false
+
+[{*.gant,*.groovy,*.gy}]
+ij_groovy_align_group_field_declarations = false
+ij_groovy_align_multiline_array_initializer_expression = false
+ij_groovy_align_multiline_assignment = false
+ij_groovy_align_multiline_binary_operation = false
+ij_groovy_align_multiline_chained_methods = false
+ij_groovy_align_multiline_extends_list = false
+ij_groovy_align_multiline_for = true
+ij_groovy_align_multiline_list_or_map = true
+ij_groovy_align_multiline_method_parentheses = false
+ij_groovy_align_multiline_parameters = true
+ij_groovy_align_multiline_parameters_in_calls = false
+ij_groovy_align_multiline_resources = true
+ij_groovy_align_multiline_ternary_operation = false
+ij_groovy_align_multiline_throws_list = false
+ij_groovy_align_named_args_in_map = true
+ij_groovy_align_throws_keyword = false
+ij_groovy_array_initializer_new_line_after_left_brace = false
+ij_groovy_array_initializer_right_brace_on_new_line = false
+ij_groovy_array_initializer_wrap = off
+ij_groovy_assert_statement_wrap = off
+ij_groovy_assignment_wrap = off
+ij_groovy_binary_operation_wrap = off
+ij_groovy_blank_lines_after_class_header = 0
+ij_groovy_blank_lines_after_imports = 1
+ij_groovy_blank_lines_after_package = 1
+ij_groovy_blank_lines_around_class = 1
+ij_groovy_blank_lines_around_field = 0
+ij_groovy_blank_lines_around_field_in_interface = 0
+ij_groovy_blank_lines_around_method = 1
+ij_groovy_blank_lines_around_method_in_interface = 1
+ij_groovy_blank_lines_before_imports = 1
+ij_groovy_blank_lines_before_method_body = 0
+ij_groovy_blank_lines_before_package = 0
+ij_groovy_block_brace_style = end_of_line
+ij_groovy_block_comment_add_space = false
+ij_groovy_block_comment_at_first_column = true
+ij_groovy_call_parameters_new_line_after_left_paren = false
+ij_groovy_call_parameters_right_paren_on_new_line = false
+ij_groovy_call_parameters_wrap = off
+ij_groovy_catch_on_new_line = false
+ij_groovy_class_annotation_wrap = split_into_lines
+ij_groovy_class_brace_style = end_of_line
+ij_groovy_class_count_to_use_import_on_demand = 5
+ij_groovy_do_while_brace_force = never
+ij_groovy_else_on_new_line = false
+ij_groovy_enable_groovydoc_formatting = true
+ij_groovy_enum_constants_wrap = off
+ij_groovy_extends_keyword_wrap = off
+ij_groovy_extends_list_wrap = off
+ij_groovy_field_annotation_wrap = split_into_lines
+ij_groovy_finally_on_new_line = false
+ij_groovy_for_brace_force = never
+ij_groovy_for_statement_new_line_after_left_paren = false
+ij_groovy_for_statement_right_paren_on_new_line = false
+ij_groovy_for_statement_wrap = off
+ij_groovy_ginq_general_clause_wrap_policy = 2
+ij_groovy_ginq_having_wrap_policy = 1
+ij_groovy_ginq_indent_having_clause = true
+ij_groovy_ginq_indent_on_clause = true
+ij_groovy_ginq_on_wrap_policy = 1
+ij_groovy_ginq_space_after_keyword = true
+ij_groovy_if_brace_force = never
+ij_groovy_import_annotation_wrap = 2
+ij_groovy_imports_layout = *, |, javax.**, java.**, |, $*
+ij_groovy_indent_case_from_switch = true
+ij_groovy_indent_label_blocks = true
+ij_groovy_insert_inner_class_imports = false
+ij_groovy_keep_blank_lines_before_right_brace = 2
+ij_groovy_keep_blank_lines_in_code = 2
+ij_groovy_keep_blank_lines_in_declarations = 2
+ij_groovy_keep_control_statement_in_one_line = true
+ij_groovy_keep_first_column_comment = true
+ij_groovy_keep_indents_on_empty_lines = false
+ij_groovy_keep_line_breaks = true
+ij_groovy_keep_multiple_expressions_in_one_line = false
+ij_groovy_keep_simple_blocks_in_one_line = false
+ij_groovy_keep_simple_classes_in_one_line = true
+ij_groovy_keep_simple_lambdas_in_one_line = true
+ij_groovy_keep_simple_methods_in_one_line = true
+ij_groovy_label_indent_absolute = false
+ij_groovy_label_indent_size = 0
+ij_groovy_lambda_brace_style = end_of_line
+ij_groovy_layout_static_imports_separately = true
+ij_groovy_line_comment_add_space = false
+ij_groovy_line_comment_add_space_on_reformat = false
+ij_groovy_line_comment_at_first_column = true
+ij_groovy_method_annotation_wrap = split_into_lines
+ij_groovy_method_brace_style = end_of_line
+ij_groovy_method_call_chain_wrap = off
+ij_groovy_method_parameters_new_line_after_left_paren = false
+ij_groovy_method_parameters_right_paren_on_new_line = false
+ij_groovy_method_parameters_wrap = off
+ij_groovy_modifier_list_wrap = false
+ij_groovy_names_count_to_use_import_on_demand = 3
+ij_groovy_packages_to_use_import_on_demand = java.awt.*, javax.swing.*
+ij_groovy_parameter_annotation_wrap = off
+ij_groovy_parentheses_expression_new_line_after_left_paren = false
+ij_groovy_parentheses_expression_right_paren_on_new_line = false
+ij_groovy_prefer_parameters_wrap = false
+ij_groovy_resource_list_new_line_after_left_paren = false
+ij_groovy_resource_list_right_paren_on_new_line = false
+ij_groovy_resource_list_wrap = off
+ij_groovy_space_after_assert_separator = true
+ij_groovy_space_after_colon = true
+ij_groovy_space_after_comma = true
+ij_groovy_space_after_comma_in_type_arguments = true
+ij_groovy_space_after_for_semicolon = true
+ij_groovy_space_after_quest = true
+ij_groovy_space_after_type_cast = true
+ij_groovy_space_before_annotation_parameter_list = false
+ij_groovy_space_before_array_initializer_left_brace = false
+ij_groovy_space_before_assert_separator = false
+ij_groovy_space_before_catch_keyword = true
+ij_groovy_space_before_catch_left_brace = true
+ij_groovy_space_before_catch_parentheses = true
+ij_groovy_space_before_class_left_brace = true
+ij_groovy_space_before_closure_left_brace = true
+ij_groovy_space_before_colon = true
+ij_groovy_space_before_comma = false
+ij_groovy_space_before_do_left_brace = true
+ij_groovy_space_before_else_keyword = true
+ij_groovy_space_before_else_left_brace = true
+ij_groovy_space_before_finally_keyword = true
+ij_groovy_space_before_finally_left_brace = true
+ij_groovy_space_before_for_left_brace = true
+ij_groovy_space_before_for_parentheses = true
+ij_groovy_space_before_for_semicolon = false
+ij_groovy_space_before_if_left_brace = true
+ij_groovy_space_before_if_parentheses = true
+ij_groovy_space_before_method_call_parentheses = false
+ij_groovy_space_before_method_left_brace = true
+ij_groovy_space_before_method_parentheses = false
+ij_groovy_space_before_quest = true
+ij_groovy_space_before_record_parentheses = false
+ij_groovy_space_before_switch_left_brace = true
+ij_groovy_space_before_switch_parentheses = true
+ij_groovy_space_before_synchronized_left_brace = true
+ij_groovy_space_before_synchronized_parentheses = true
+ij_groovy_space_before_try_left_brace = true
+ij_groovy_space_before_try_parentheses = true
+ij_groovy_space_before_while_keyword = true
+ij_groovy_space_before_while_left_brace = true
+ij_groovy_space_before_while_parentheses = true
+ij_groovy_space_in_named_argument = true
+ij_groovy_space_in_named_argument_before_colon = false
+ij_groovy_space_within_empty_array_initializer_braces = false
+ij_groovy_space_within_empty_method_call_parentheses = false
+ij_groovy_spaces_around_additive_operators = true
+ij_groovy_spaces_around_assignment_operators = true
+ij_groovy_spaces_around_bitwise_operators = true
+ij_groovy_spaces_around_equality_operators = true
+ij_groovy_spaces_around_lambda_arrow = true
+ij_groovy_spaces_around_logical_operators = true
+ij_groovy_spaces_around_multiplicative_operators = true
+ij_groovy_spaces_around_regex_operators = true
+ij_groovy_spaces_around_relational_operators = true
+ij_groovy_spaces_around_shift_operators = true
+ij_groovy_spaces_within_annotation_parentheses = false
+ij_groovy_spaces_within_array_initializer_braces = false
+ij_groovy_spaces_within_braces = true
+ij_groovy_spaces_within_brackets = false
+ij_groovy_spaces_within_cast_parentheses = false
+ij_groovy_spaces_within_catch_parentheses = false
+ij_groovy_spaces_within_for_parentheses = false
+ij_groovy_spaces_within_gstring_injection_braces = false
+ij_groovy_spaces_within_if_parentheses = false
+ij_groovy_spaces_within_list_or_map = false
+ij_groovy_spaces_within_method_call_parentheses = false
+ij_groovy_spaces_within_method_parentheses = false
+ij_groovy_spaces_within_parentheses = false
+ij_groovy_spaces_within_switch_parentheses = false
+ij_groovy_spaces_within_synchronized_parentheses = false
+ij_groovy_spaces_within_try_parentheses = false
+ij_groovy_spaces_within_tuple_expression = false
+ij_groovy_spaces_within_while_parentheses = false
+ij_groovy_special_else_if_treatment = true
+ij_groovy_ternary_operation_wrap = off
+ij_groovy_throws_keyword_wrap = off
+ij_groovy_throws_list_wrap = off
+ij_groovy_use_flying_geese_braces = false
+ij_groovy_use_fq_class_names = false
+ij_groovy_use_fq_class_names_in_javadoc = true
+ij_groovy_use_relative_indents = false
+ij_groovy_use_single_class_imports = true
+ij_groovy_variable_annotation_wrap = off
+ij_groovy_while_brace_force = never
+ij_groovy_while_on_new_line = false
+ij_groovy_wrap_chain_calls_after_dot = false
+ij_groovy_wrap_long_lines = false
+
+[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}]
+indent_size = 2
+ij_json_array_wrapping = split_into_lines
+ij_json_keep_blank_lines_in_code = 0
+ij_json_keep_indents_on_empty_lines = false
+ij_json_keep_line_breaks = true
+ij_json_keep_trailing_comma = false
+ij_json_object_wrapping = split_into_lines
+ij_json_property_alignment = do_not_align
+ij_json_space_after_colon = true
+ij_json_space_after_comma = true
+ij_json_space_before_colon = false
+ij_json_space_before_comma = false
+ij_json_spaces_within_braces = false
+ij_json_spaces_within_brackets = false
+ij_json_wrap_long_lines = false
+
+[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}]
+ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
+ij_html_align_attributes = true
+ij_html_align_text = false
+ij_html_attribute_wrap = normal
+ij_html_block_comment_add_space = false
+ij_html_block_comment_at_first_column = true
+ij_html_do_not_align_children_of_min_lines = 0
+ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p
+ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot
+ij_html_enforce_quotes = false
+ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var
+ij_html_keep_blank_lines = 2
+ij_html_keep_indents_on_empty_lines = false
+ij_html_keep_line_breaks = true
+ij_html_keep_line_breaks_in_text = true
+ij_html_keep_whitespaces = false
+ij_html_keep_whitespaces_inside = span, pre, textarea
+ij_html_line_comment_at_first_column = true
+ij_html_new_line_after_last_attribute = never
+ij_html_new_line_before_first_attribute = never
+ij_html_quote_style = double
+ij_html_remove_new_line_before_tags = br
+ij_html_space_after_tag_name = false
+ij_html_space_around_equality_in_attribute = false
+ij_html_space_inside_empty_tag = false
+ij_html_text_wrap = normal
+
+[{*.jsf,*.jsp,*.jspf,*.tag,*.tagf,*.xjsp}]
+ij_jsp_jsp_prefer_comma_separated_import_list = false
+ij_jsp_keep_indents_on_empty_lines = false
+
+[{*.jspx,*.tagx}]
+ij_jspx_keep_indents_on_empty_lines = false
+
+[{*.kt,*.kts}]
+ij_kotlin_align_in_columns_case_branch = false
+ij_kotlin_align_multiline_binary_operation = false
+ij_kotlin_align_multiline_extends_list = false
+ij_kotlin_align_multiline_method_parentheses = false
+ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters_in_calls = false
+ij_kotlin_allow_trailing_comma = false
+ij_kotlin_allow_trailing_comma_on_call_site = false
+ij_kotlin_assignment_wrap = off
+ij_kotlin_blank_lines_after_class_header = 0
+ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_add_space = false
+ij_kotlin_block_comment_at_first_column = true
+ij_kotlin_call_parameters_new_line_after_left_paren = false
+ij_kotlin_call_parameters_right_paren_on_new_line = false
+ij_kotlin_call_parameters_wrap = off
+ij_kotlin_catch_on_new_line = false
+ij_kotlin_class_annotation_wrap = split_into_lines
+ij_kotlin_continuation_indent_for_chained_calls = true
+ij_kotlin_continuation_indent_for_expression_bodies = true
+ij_kotlin_continuation_indent_in_argument_lists = true
+ij_kotlin_continuation_indent_in_elvis = true
+ij_kotlin_continuation_indent_in_if_conditions = true
+ij_kotlin_continuation_indent_in_parameter_lists = true
+ij_kotlin_continuation_indent_in_supertype_lists = true
+ij_kotlin_else_on_new_line = false
+ij_kotlin_enum_constants_wrap = off
+ij_kotlin_extends_list_wrap = off
+ij_kotlin_field_annotation_wrap = split_into_lines
+ij_kotlin_finally_on_new_line = false
+ij_kotlin_if_rparen_on_new_line = false
+ij_kotlin_import_nested_classes = false
+ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^
+ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
+ij_kotlin_keep_blank_lines_before_right_brace = 2
+ij_kotlin_keep_blank_lines_in_code = 2
+ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_first_column_comment = true
+ij_kotlin_keep_indents_on_empty_lines = false
+ij_kotlin_keep_line_breaks = true
+ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_break_after_multiline_when_entry = true
+ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_add_space_on_reformat = false
+ij_kotlin_line_comment_at_first_column = true
+ij_kotlin_method_annotation_wrap = split_into_lines
+ij_kotlin_method_call_chain_wrap = off
+ij_kotlin_method_parameters_new_line_after_left_paren = false
+ij_kotlin_method_parameters_right_paren_on_new_line = false
+ij_kotlin_method_parameters_wrap = off
+ij_kotlin_name_count_to_use_star_import = 5
+ij_kotlin_name_count_to_use_star_import_for_members = 3
+ij_kotlin_packages_to_use_import_on_demand = java.util.*, kotlinx.android.synthetic.**, io.ktor.**
+ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_space_after_comma = true
+ij_kotlin_space_after_extend_colon = true
+ij_kotlin_space_after_type_colon = true
+ij_kotlin_space_before_catch_parentheses = true
+ij_kotlin_space_before_comma = false
+ij_kotlin_space_before_extend_colon = true
+ij_kotlin_space_before_for_parentheses = true
+ij_kotlin_space_before_if_parentheses = true
+ij_kotlin_space_before_lambda_arrow = true
+ij_kotlin_space_before_type_colon = false
+ij_kotlin_space_before_when_parentheses = true
+ij_kotlin_space_before_while_parentheses = true
+ij_kotlin_spaces_around_additive_operators = true
+ij_kotlin_spaces_around_assignment_operators = true
+ij_kotlin_spaces_around_equality_operators = true
+ij_kotlin_spaces_around_function_type_arrow = true
+ij_kotlin_spaces_around_logical_operators = true
+ij_kotlin_spaces_around_multiplicative_operators = true
+ij_kotlin_spaces_around_range = false
+ij_kotlin_spaces_around_relational_operators = true
+ij_kotlin_spaces_around_unary_operator = false
+ij_kotlin_spaces_around_when_arrow = true
+ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_while_on_new_line = false
+ij_kotlin_wrap_elvis_expressions = 1
+ij_kotlin_wrap_expression_body_functions = 0
+ij_kotlin_wrap_first_method_in_call_chain = false
+
+[{*.markdown,*.md}]
+ij_markdown_force_one_space_after_blockquote_symbol = true
+ij_markdown_force_one_space_after_header_symbol = true
+ij_markdown_force_one_space_after_list_bullet = true
+ij_markdown_force_one_space_between_words = true
+ij_markdown_insert_quote_arrows_on_wrap = true
+ij_markdown_keep_indents_on_empty_lines = false
+ij_markdown_keep_line_breaks_inside_text_blocks = true
+ij_markdown_max_lines_around_block_elements = 1
+ij_markdown_max_lines_around_header = 1
+ij_markdown_max_lines_between_paragraphs = 1
+ij_markdown_min_lines_around_block_elements = 1
+ij_markdown_min_lines_around_header = 1
+ij_markdown_min_lines_between_paragraphs = 1
+ij_markdown_wrap_text_if_long = true
+ij_markdown_wrap_text_inside_blockquotes = true
+
+[{*.pb,*.textproto}]
+indent_size = 2
+tab_width = 2
+ij_continuation_indent_size = 4
+ij_prototext_keep_blank_lines_in_code = 2
+ij_prototext_keep_indents_on_empty_lines = false
+ij_prototext_keep_line_breaks = true
+ij_prototext_space_after_colon = true
+ij_prototext_space_after_comma = true
+ij_prototext_space_before_colon = false
+ij_prototext_space_before_comma = false
+ij_prototext_spaces_within_braces = true
+ij_prototext_spaces_within_brackets = false
+
+[{*.properties,spring.handlers,spring.schemas}]
+ij_properties_align_group_field_declarations = false
+ij_properties_keep_blank_lines = false
+ij_properties_key_value_delimiter = equals
+ij_properties_spaces_around_key_value_delimiter = false
+
+[{*.qute.htm,*.qute.html,*.qute.json,*.qute.txt,*.qute.yaml,*.qute.yml}]
+ij_qute_keep_indents_on_empty_lines = false
+
+[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}]
+ij_toml_keep_indents_on_empty_lines = false
+
+[{*.yaml,*.yml}]
+indent_size = 2
+ij_yaml_align_values_properties = do_not_align
+ij_yaml_autoinsert_sequence_marker = true
+ij_yaml_block_mapping_on_new_line = false
+ij_yaml_indent_sequence_value = true
+ij_yaml_keep_indents_on_empty_lines = false
+ij_yaml_keep_line_breaks = true
+ij_yaml_sequence_on_new_line = false
+ij_yaml_space_before_colon = false
+ij_yaml_spaces_within_braces = true
+ij_yaml_spaces_within_brackets = true
diff --git a/.gitignore b/.gitignore
index 549e00a2a96fa9d7c5dbc9859664a78d980158c2..66cba166c656cd93079d45dcd0c15921d3319d5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,10 @@
-HELP.md
 target/
 !.mvn/wrapper/maven-wrapper.jar
 !**/src/main/**/target/
 !**/src/test/**/target/
 
+output-data/
+
 ### STS ###
 .apt_generated
 .classpath
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cb282f6d42063de00c779dc53ba1f0ecb4d73424
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,49 @@
+image: maven:latest
+
+cache:
+  paths:
+    - .m2/repository
+
+variables:
+  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
+  MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version"
+
+default:
+  tags:
+    - shared-fi
+
+stages:
+  - build
+  - tests
+
+build:
+  stage: build
+  script:
+    - echo "We are building your project"
+    - mvn clean install $MAVEN_CLI_OPTS -DskipTests
+
+unit_test:
+  stage: tests
+  script:
+    - echo "We are testing your project build with unit tests"
+    - mvn test $MAVEN_CLI_OPTS
+  artifacts:
+    expire_in: 10 min
+    paths:
+      - "*/target/surefire-reports/*"
+    reports:
+      junit:
+        - "*/target/surefire-reports/*.xml"
+
+integration_test:
+  stage: tests
+  script:
+    - echo "We are testing your project build with integration tests"
+    - mvn verify $MAVEN_CLI_OPTS
+  artifacts:
+    expire_in: 10 min
+    paths:
+      - "*/target/failsafe-reports/*"
+    reports:
+      junit:
+        - "*/target/failsafe-reports/*.xml"
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..da0d673f83113447e9fcc35dba874a054f37cf9d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,44 @@
+# Define the base image
+FROM maven:3.9.0-eclipse-temurin-17-alpine AS build
+
+# Copy the pom.xml parent file and module dirs
+COPY pom.xml /build/pom.xml
+COPY application /build/application
+COPY core /build/core
+COPY notification /build/notification
+COPY visualization /build/visualization
+
+WORKDIR /build
+RUN mvn clean install -DskipTests
+
+FROM eclipse-temurin:17-jre-focal AS pa165-formula-team-management-application
+
+COPY --from=build /build/application/target/*.jar ./application.jar
+
+# Expose ports and set entry point
+EXPOSE 8081
+CMD ["java", "-jar", "application.jar"]
+
+FROM eclipse-temurin:17-jre-focal AS pa165-formula-team-management-core
+
+COPY --from=build /build/core/target/*.jar ./core.jar
+
+# Expose ports and set entry point
+EXPOSE 8090
+CMD ["java", "-jar", "core.jar"]
+
+FROM eclipse-temurin:17-jre-focal AS pa165-formula-team-management-notification
+
+COPY --from=build /build/notification/target/*.jar ./notification.jar
+
+# Expose ports and set entry point
+EXPOSE 8083
+CMD ["java", "-jar", "notification.jar"]
+
+FROM eclipse-temurin:17-jre-focal AS pa165-formula-team-management-visualization
+
+COPY --from=build /build/visualization/target/*.jar ./visualization.jar
+
+# Expose ports and set entry point
+EXPOSE 8082
+CMD ["java", "-jar", "visualization.jar"]
\ No newline at end of file
diff --git a/README.md b/README.md
index a468d7736b91b2b6517fe3a3d8413b104aa3d4c1..d249e0e11de6ce18ad1c55b4b1f028ebb1c8cbae 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,275 @@
 # Formula team management
 
-TODO
+## Navigation
+- [About Project](#about-project)
+- [Build and run the app](#build-and-run-the-app)
+- [Seed and clear DB](#seed-and-clear-db)
+- [Build and run the app with Docker](#build-and-run-the-app-with-docker)
+- [Collecting Metrics](#collecting-and-displaying-metrics)
+- [Grafana](#grafana-setup)
+- [Runnable scenario](#runnable-scenario)
 
-# Pro vývoj
+## About Project
 
-Nemělo by být potřeba upravovat pom.xml nikde, pouze v formula-team-management v sekci modules pro přidání nových modulů.
+- **Name**: Formula One Team
 
-V openapi.yaml si nejprve specifikujte tag, který reprezentuje classu, do které se vygenerujou vaše metody např. CarService.
-Tento tag pak používejte k označení metod, ať se všechny negenerujou do jedné obří classy, ale jsou pěkně rozdělené.
+- **Technologies**: Java 17, Spring, Maven, Tomcat
+
+- **Developers**:
+
+    - Alžbeta Hajná [xhajna](https://gitlab.fi.muni.cz/xhajna)
+    - Jitka Viceníková [xvicenik](https://gitlab.fi.muni.cz/xvicenik)
+    - Andrej Žabka [xzabka](https://gitlab.fi.muni.cz/xzabka)
+    - Michal Badin [xbadin](https://gitlab.fi.muni.cz/xbadin)
+
+- **Assigment**:
+    - A Formula 1 team is in need to manage its participation to the world championship. The team has two cars
+      participating to each race, each one driven by one driver, with other test drivers available to develop the car
+      further. The manager of the team can set the driver for each car and manage a list of other (test) drivers that
+      are under contract and he can also set as main drivers if unhappy about results. Each driver has a name, surname,
+      nationality, and set of characteristics (e.g. driving on the wet, aggressiveness, etc..) that the manager can
+      visualize. The car is composed of several components (engine, suspensions), that the different departments of the
+      team (e.g. engine, aerodynamics) evolve during time. The manager can pick from a list of components and assemble
+      the cars that will participate to the next race. Engineers from other departments can login to the system and put
+      newer components available. The manager is notified that those components are available and that they can be used
+      to improve the car.
+
+- **Modules**:
+  <br>Our application is divided into 4 separate spring boot modules, as shown on the class diagram
+
+1) Core module:
+   <br>Core module contains most of the functionality of the project assignment. It communicates with the other modules
+   via their REST api's.
+2) Notifications module:
+   <br>Notifications module sends e-mail (or other) notifications to team members.
+   <br>
+   - Receives e-mails are determined based on provided e-mail during the creation of new applications and e-mails defined in 
+   **application.properties** in **core** and **application modules**.
+3) Visualisation module:
+   <br>Visualization module provides a visualization of given JSON data in readable HTML (or other) format. Team manager
+   will be able to visualize current state of both cars.
+   - The result can be downloaded via Swagger UI or directly accessed in the module's folder **output-data**, which is created
+   in case of missing.
+4) Applications module:
+   <br>Applications module allows people to send an application to this team. Manager can view these applications and
+   hire new drivers for his team.
+
+- **Diagrams**:
+
+![IMAGE_DESCRIPTION](diagrams/class_diagram.png)
+![IMAGE_DESCRIPTION](diagrams/use_case_diagram.png)
+
+# Build and run the app
+
+Build the app (all modules) by running
 
-Vygenerování se spouští normálně pomocí:
 ```bash
 mvn clean install
 ```
-Pokud tam budete mít chyby, tak přepínač -e je srozumitelně vypisuje.
 
-Appka se spustí pomocí:
+in the project's parent directory, or build separate modules by running the same command inside the module's parent
+directory. For example, to build notifications module:
+
+```bash
+cd notification
+mvn clean install
+```
+
+Tests are run automatically during the build, but if you want to run them again, use:
+
+```bash
+mvn test
+```
+
+again, either in the parent directory to test all modules, or inside separate modules to only test one at a time.
+
+To run a module, just run:
+
 ```bash
 cd core
 mvn
 ```
 
-A potom si to možete vyzkušat přes swagger na adrese http://localhost:8080/swagger-ui/index.html.
+To see the running REST api's in action, use CURL or swagger UI:
+http://localhost:8090/swagger-ui/index.html
+
+Just note that each module runs on a different port by default, so care where you send your requests.<br>
+**Default ports:**
+
+- core module: 8090
+- notifications module: 8083
+- visualization module: 8082
+- applications module: 8081
+
+These ports can be overridden either in the application.properties file of each module, or by specifying the port
+manually when running the "mvn" command to run the module.
+
+## Seed and clear DB
+DB is at the start of the app seeded with some Data, thus you don't have to do it manually. 
+For users are available 2 endpoints:
+
+- /seed - Seed DB with some Data
+- /clear - Clear DB whenever during running
+
+*Note: /seed will always seed DB with the same Data. Keep in mind, If you seed DB after startup, DB will contain the same data twice.*
+
+## Build and run the app with Docker
+
+For purpose of this build and run the installation of Docker/Podman
+is required, steps to do so are not provided here.
+
+*Note: If you are a win user using Docker Desktop, don't forget to run Docker Desktop first.*
+
+### Building Docker images
+
+To build individual docker images
+
+```bash
+docker build -t pa165-formula-team-management-<module> --target pa165-formula-team-management-<module> .
+```
+
+*Note: make sure you are in root directory of this project*
+
+### Running Docker images
+
+To run specific built docker image use command
+
+```bash
+docker run -p 8090:8090 -e DOCKER='1' pa165-formula-team-management-<module>
+```
+
+To avoid confusion the best way is to be consistent with ports listed above,
+e.g. for running `application` image use:
+
+```bash
+docker run -p 8081:8081 -e DOCKER='1' pa165-formula-team-management-application
+```
+
+### Docker compose
+
+To run all modules at once in one container, you can run (in root directory)
+
+```bash
+docker-compose up
+```
+
+*Note: in case you want to also rebuild images, use flag `--build`*
+*Note: use flag `-d` for detached mode*
+
+To stop container run
+
+```bash
+docker-compose down
+```
+
+## Collecting and displaying Metrics
+
+To collect the metrics in an automated way we use Prometheus,
+Grafana is used for displaying the most useful metrics.
+
+Both services are running automatically in docker container together with other modules,
+ when you run the docker-compose.<br>
+You can check prometheus UI at http://localhost:9090.<br>
+You can see grafana at http://localhost:3000
+
+### Grafana setup
+
+Credentials to log into Grafana are:
+- user `admin`
+- password `admin`
+
+Two dashboards are imported automatically, but feel free to experiment and see different metrics.
+
+## Runnable scenario
+
+To showcase our system, we defined two scenarios:<br>
+- Engineer, manager and their everyday business
+- Applications module load test
+
+### Engineer, manager and their everyday business
+Our system is a management system for a formula 1 team. It is designed to help engineers and manager
+to keep track of drivers, car components and current state of the cars. Naturally, this system will
+not experience any high loads, so (in the core module) there is no reason to simulate user behaviour
+with any kind of script. The best way to test the system is to manually try it.
+
+#### 1. Become a manager of our F1 team (optional)
+Navigate to ``/core/src/main/resources/application.properties`` file and add your e-mail address to the
+``notification.receivers`` field. You can either delete the address that's already there, or add your own
+separated with a comma(,). This means that you will get e-mail notifications same as the manager would.
+
+#### 2. Run all modules
+```bash
+docker-compose up --build
+```
+Now, all the modules, including prometheus and grafana should be available through your web browser.
+Feel free to play around with every module's swagger UI. Ports of all modules are listed above.<br>
+Also feel free to monitor the system through grafana during all the following steps.
+
+#### 3. Log in as an engineer and create a new component
+- Visit swagger UI of core module at http://localhost:8090/swagger-ui/index.html. 
+- Check, that without logging in, you are unable to perform any operations, except the /seed and /clear. 
+- Log in with the authorize button and select the engineer scope (test_1). 
+- Now, you are able to perform only the operations at the ``/carComponent`` endpoint.
+- Create a new component that you just made with your team of engineers at your lab.
+- Log out from the engineer scope.
+
+If you followed step 1, you (as a manager) should also get an e-mail notification about 
+new component being created. 
+
+*Note: It is possible to time-out our team's e-mail address if you make too many new notifications (for example during the second scenario - load test). 
+So if the Notifications module is not working, try to wait a few minutes, maybe another reviewer
+was doing a load test at the same time. In real system we would not use a regular Gmail address for such job - there would be no timeouts.*
+
+#### 4. Log in as an manager and create a visualization of a car
+- Log in using a manager scope (test_5), same way as before.
+- Now, you can perform all operations, except POST at /carComponent (only engineers can do that).
+- Check, that the new component created by the engineer is really in the system.
+- Play around with the other endpoints, for example try to change the driver in one of the cars. 
+(If you cleared the database before, seed it so you don't have to create everything manually)
+- Now call the GET operation on a certain car, this operation returns the car as a JSON directly in the response,
+but also calls the Visualization module with that car.
+- Check ``/visualization/output-data`` and you should be able to see a PDF visualization of the car you chose.
+- Hopefully you chose the right components and drivers for your cars. Good luck in the next race.
+
+This scenario showcased the functionality of the Core module and it's communication with Notification and Visualization modules.
+
+### Applications module load test:
+This scenario showcases a situation, where lots of people are sending applications to this F1 team.<br>
+At the same time, some admin (someone from HR for example) is randomly accepting/rejecting these applications.<br>
+
+Before running this scenario make sure you have installed ``python`` and ``locust`` module, which can be installed with<br>
+``pip install locust``
+
+#### 1. Run all modules
+
+```bash
+docker-compose up
+```
+
+#### 2. Run script
+
+```bash
+cd scenario-locust
+locust -f scenario.py --host http://localhost:8081 --users 2
+```
+*Note: Script can be run without arguments, but then you have them fill up in the next step.*
+
+#### 3. Open in web browser
+
+```bash
+http://localhost:8089/
+```
+Adjust ``Number of users`` and ``Spawn rate`` to test different scenarios.
+<br>*Note: At least 2 users are required  to simulate ``Admin`` and ``User`` simultaneously*
+<br>*Note: !Do not change Host!*
+
+#### 4. Collecting and displaying Metrics
+After started swarming in web browser, locust provides several pages to display various data.
+Feel free to explore them, but two main pages are ``Statistics`` and ``Charts``
+
+If you want to display more complex metrics, do not hesitate to use:
+- ``Grafana`` http://localhost:9090
+- ``Prometheus`` http://localhost:3000
+
+#### 5. Clear Data - optional
+Don't forget to clear your Application DB via ``/clear`` end point. 
\ No newline at end of file
diff --git a/application/.gitignore b/application/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..549e00a2a96fa9d7c5dbc9859664a78d980158c2
--- /dev/null
+++ b/application/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/application/.mvn/wrapper/maven-wrapper.jar b/application/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000000000000000000000000000000000000..bf82ff01c6cdae4a1bb754a6e062954d77ac5c11
Binary files /dev/null and b/application/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/application/.mvn/wrapper/maven-wrapper.properties b/application/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000000000000000000000000000000000..ca5ab4bab121c408ae246f6ef5fae23b58db2d65
--- /dev/null
+++ b/application/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
diff --git a/application/mvnw b/application/mvnw
new file mode 100644
index 0000000000000000000000000000000000000000..8a8fb2282df5b8f7263470a5a2dc0e196f35f35f
--- /dev/null
+++ b/application/mvnw
@@ -0,0 +1,316 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /usr/local/etc/mavenrc ] ; then
+    . /usr/local/etc/mavenrc
+  fi
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`\\unset -f command; \\command -v java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    else
+      jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+        else
+            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+        fi
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl -o "$wrapperJarPath" "$jarUrl" -f
+        else
+            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+        fi
+
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  $MAVEN_DEBUG_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" \
+  "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/application/mvnw.cmd b/application/mvnw.cmd
new file mode 100644
index 0000000000000000000000000000000000000000..1d8ab018eaf11d9b3a4a90e7818ace373dfbb380
--- /dev/null
+++ b/application/mvnw.cmd
@@ -0,0 +1,188 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %DOWNLOAD_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+  %JVM_CONFIG_MAVEN_PROPS% ^
+  %MAVEN_OPTS% ^
+  %MAVEN_DEBUG_OPTS% ^
+  -classpath %WRAPPER_JAR% ^
+  "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/application/openapi.yaml b/application/openapi.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b53ae168b2a36251d71568da139505eb53934226
--- /dev/null
+++ b/application/openapi.yaml
@@ -0,0 +1,221 @@
+openapi: 3.0.1
+info:
+  title: Application module
+  description: |
+    Hand-made OpenAPI document.
+  version: 1.0.0
+servers:
+  - url: "http://localhost:8081"
+tags:
+  - name: ApplicationService
+  - name: DBManagementService
+components:
+  schemas:
+    ApplicationStatus:
+      type: string
+      description: Status of application
+      enum:
+        - pending
+        - rejected
+        - accepted
+
+    ApplicationDto:
+      type: object
+      title: Application
+      description: Application for becoming driver
+      required:
+        - id
+        - name
+        - surname
+        - fillingOutDate
+        - email
+      properties:
+        id:
+          type: integer
+          description: Id of the application
+          example: 1
+          format: int64
+        name:
+          type: string
+          description: Name of the person
+          example: Max
+          maxLength: 35
+        surname:
+          type: string
+          description: Surname of the person
+          example: Verstappen
+          maxLength: 35
+        birthday:
+          type: string
+          description: Date of birth
+          example: 09-30-1997
+          format: date
+        email:
+          type: string
+          description: Email address of the person
+        fillingOutDate:
+          type: string
+          description: Date of filling out the application
+          example: 09-30-1997
+          format: date
+        status:
+          $ref: '#/components/schemas/ApplicationStatus'
+    ApplicationCreateDto:
+      type: object
+      properties:
+        name:
+          type: string
+          description: Name of the person
+          maxLength: 35
+        surname:
+          type: string
+          description: Name of the person
+          maxLength: 35
+        birthday:
+          type: string
+          description: Date of birth
+          example: 1977-09-30
+          format: date
+        email:
+          type: string
+          description: Email address of the person
+      required:
+        - name
+        - surname
+        - email
+
+  responses:
+    NotFound:
+      description: The specified resource was not found
+      content:
+        application/json:
+          schema:
+            type: object
+            title: NotFound
+            properties:
+              message:
+                type: string
+    Unexpected:
+      description: Unexpected error
+      content:
+        application/json:
+          schema:
+            type: object
+            title: Unexpected
+            properties:
+              message:
+                type: string
+    Successful:
+      description: Successfully processed
+      content:
+        application/json:
+          schema:
+            type: object
+            title: Successfully processed
+            properties:
+              message:
+                type: string
+
+paths:
+  /application:
+    post:
+      tags:
+        - ApplicationService
+      summary: Create a application
+      operationId: createApplication
+      requestBody:
+        description: Request body for creating a new application
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/ApplicationCreateDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ApplicationDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    get:
+      tags:
+        - ApplicationService
+      summary: Returns all applications
+      operationId: getAllApplications
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/ApplicationDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+
+  /application/{id}:
+    get:
+      tags:
+        - ApplicationService
+      summary: Returns an application
+      operationId: getApplication
+      parameters:
+        - { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: "Id of an application" }
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ApplicationDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+
+  /application/status:
+    put:
+      tags:
+        - ApplicationService
+      summary: Set application status
+      operationId: setStatus
+      parameters:
+        - { name: id, in: query, required: true, schema: { type: integer, format: int64 }, description: "Id of an application" }
+        - { name: applicationStatus, in: query, required: true, schema: { $ref: '#/components/schemas/ApplicationStatus' }, description: "Application status" }
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ApplicationDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+
+  /seed:
+    post:
+      tags:
+        - DBManagementService
+      summary: Seed DB with data
+      operationId: seedDB
+      responses:
+        "200":
+          $ref: '#/components/responses/Successful'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /clear:
+    post:
+      tags:
+        - DBManagementService
+      summary: Clear DB
+      operationId: clearDB
+      responses:
+        "200":
+          $ref: '#/components/responses/Successful'
+        default:
+          $ref: '#/components/responses/Unexpected'
\ No newline at end of file
diff --git a/application/pom.xml b/application/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..68e172032e0bda65aeb8b57e7a8845fbd644b74b
--- /dev/null
+++ b/application/pom.xml
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>formula-team-management</artifactId>
+        <groupId>cz.muni.pa165</groupId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>application</artifactId>
+    <packaging>jar</packaging>
+    <name>Application module</name>
+    <description>Application generated using OpenAPI Generator "java"</description>
+
+    <build>
+        <defaultGoal>spring-boot:run</defaultGoal>
+        <!-- name of executable JAR file -->
+        <finalName>application_module</finalName>
+
+        <plugins>
+            <plugin>
+                <groupId>org.openapitools</groupId>
+                <artifactId>openapi-generator-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            <inputSpec>${project.basedir}/openapi.yaml</inputSpec>
+                            <generatorName>spring</generatorName>
+                            <apiPackage>cz.muni.pa165.generated.api</apiPackage>
+                            <modelPackage>cz.muni.pa165.generated.model</modelPackage>
+                            <!-- https://openapi-generator.tech/docs/generators/spring -->
+                            <configOptions>
+                                <basePackage>cz.muni.pa165</basePackage>
+                                <configPackage>cz.muni.pa165.generated.config</configPackage>
+                                <useSpringBoot3>true</useSpringBoot3>
+                                <useTags>true</useTags>
+                                <delegatePattern>true</delegatePattern>
+                            </configOptions>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <!-- create executable jar -->
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <executable>true</executable>
+                </configuration>
+            </plugin>
+            <!-- run integration tests in "mvn verify" phase -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M7</version>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.validation</groupId>
+            <artifactId>jakarta.validation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-models-jakarta</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations-jakarta</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openapitools</groupId>
+            <artifactId>jackson-databind-nullable</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+
+        <!-- for including Swagger UI -->
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+        </dependency>
+
+        <!-- for testing -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-tx</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <version>2.7.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.data</groupId>
+            <artifactId>spring-data-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.persistence</groupId>
+            <artifactId>jakarta.persistence-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-processor</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
+
+        <!--		Actuator dependency-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+
+        <!--		Prometheus dependency -->
+        <dependency>
+            <groupId>io.micrometer</groupId>
+            <artifactId>micrometer-registry-prometheus</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webflux</artifactId>
+            <version>6.0.6</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/application/src/main/java/cz/muni/pa165/config/ServiceConfig.java b/application/src/main/java/cz/muni/pa165/config/ServiceConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..659bb487e2103f30814d3e18d8e72b04074f664c
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/config/ServiceConfig.java
@@ -0,0 +1,20 @@
+package cz.muni.pa165.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+import org.springframework.web.reactive.function.client.WebClient;
+
+/**
+ * @author Michal Badin
+ */
+@Configuration
+@ComponentScan
+@EnableTransactionManagement
+public class ServiceConfig {
+    @Bean
+    public WebClient.Builder getWebClientBuilder() {
+        return WebClient.builder();
+    }
+}
\ No newline at end of file
diff --git a/application/src/main/java/cz/muni/pa165/data/enums/ApplicationStatusEnum.java b/application/src/main/java/cz/muni/pa165/data/enums/ApplicationStatusEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..02c0421eeafa7c3157434dc1fd48811df51a03cf
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/data/enums/ApplicationStatusEnum.java
@@ -0,0 +1,41 @@
+package cz.muni.pa165.data.enums;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * @author Michal Badin
+ */
+public enum ApplicationStatusEnum {
+    PENDING("pending"),
+
+    REJECTED("rejected"),
+
+    ACCEPTED("accepted");
+
+    private final String value;
+
+    ApplicationStatusEnum(String value) {
+        this.value = value;
+    }
+
+    @JsonCreator
+    public static ApplicationStatusEnum fromValue(String value) {
+        for (ApplicationStatusEnum s : ApplicationStatusEnum.values()) {
+            if (s.value.equals(value)) {
+                return s;
+            }
+        }
+        throw new IllegalArgumentException("Unexpected value '" + value + "'");
+    }
+
+    @JsonValue
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(value);
+    }
+}
\ No newline at end of file
diff --git a/application/src/main/java/cz/muni/pa165/data/model/Application.java b/application/src/main/java/cz/muni/pa165/data/model/Application.java
new file mode 100644
index 0000000000000000000000000000000000000000..578ee814be2e0b1b3992ba684129a0c529561387
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/data/model/Application.java
@@ -0,0 +1,128 @@
+package cz.muni.pa165.data.model;
+
+import cz.muni.pa165.data.enums.ApplicationStatusEnum;
+import jakarta.persistence.*;
+import jakarta.validation.constraints.*;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.util.Objects;
+
+/**
+ * @author Michal Badin
+ */
+@Entity
+@Table(name = "application")
+public class Application implements Serializable {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Enumerated(EnumType.STRING)
+    private ApplicationStatusEnum status;
+
+    @NotEmpty
+    @Size(max = 35)
+    private String name;
+
+    @NotEmpty
+    @Size(max = 35)
+    private String surname;
+
+    @Past
+    @NotNull
+    private LocalDate birthday;
+
+    @Email
+    private String email;
+
+    @Column(name = "filling_out_date")
+    @PastOrPresent
+    private LocalDate fillingOutDate;
+
+    public Application() {
+    }
+
+    public Application(ApplicationStatusEnum status, String name, String surname, LocalDate birthday, String email, LocalDate fillingOutDate) {
+        this.status = status;
+        this.name = name;
+        this.surname = surname;
+        this.birthday = birthday;
+        this.email = email;
+        this.fillingOutDate = fillingOutDate;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public ApplicationStatusEnum getStatus() {
+        return status;
+    }
+
+    public void setStatus(ApplicationStatusEnum status) {
+        this.status = status;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getSurname() {
+        return surname;
+    }
+
+    public void setSurname(String surname) {
+        this.surname = surname;
+    }
+
+    public LocalDate getBirthday() {
+        return birthday;
+    }
+
+    public void setBirthday(LocalDate birthday) {
+        this.birthday = birthday;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public LocalDate getFillingOutDate() {
+        return fillingOutDate;
+    }
+
+    public void setFillingOutDate(LocalDate fillingOutDate) {
+        this.fillingOutDate = fillingOutDate;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof Application application)) {
+            return false;
+        }
+        return Objects.equals(getName(), application.getName()) &&
+                Objects.equals(getSurname(), application.getSurname()) &&
+                Objects.equals(getBirthday(), application.getBirthday());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getName(), getSurname(), getBirthday());
+    }
+}
diff --git a/application/src/main/java/cz/muni/pa165/data/repository/ApplicationRepository.java b/application/src/main/java/cz/muni/pa165/data/repository/ApplicationRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..f60e3f6085894c254ea7d7417f4d8f39010480b1
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/data/repository/ApplicationRepository.java
@@ -0,0 +1,12 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.model.Application;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author Michal Badin
+ */
+@Repository
+public interface ApplicationRepository extends JpaRepository<Application, Long> {
+}
diff --git a/application/src/main/java/cz/muni/pa165/exceptions/ResourceNotFoundException.java b/application/src/main/java/cz/muni/pa165/exceptions/ResourceNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..792816b0ddfc4fd11d4a2ee6a3227dbe919e1a96
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/exceptions/ResourceNotFoundException.java
@@ -0,0 +1,10 @@
+package cz.muni.pa165.exceptions;
+
+/**
+ * @author Michal Badin
+ */
+public class ResourceNotFoundException extends RuntimeException {
+    public ResourceNotFoundException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file
diff --git a/application/src/main/java/cz/muni/pa165/facade/ApplicationFacade.java b/application/src/main/java/cz/muni/pa165/facade/ApplicationFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..41d09585b6df2b2a7a7d94370dd73ab7d2dcd570
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/facade/ApplicationFacade.java
@@ -0,0 +1,48 @@
+package cz.muni.pa165.facade;
+
+import cz.muni.pa165.data.enums.ApplicationStatusEnum;
+import cz.muni.pa165.generated.model.ApplicationCreateDto;
+import cz.muni.pa165.generated.model.ApplicationDto;
+import cz.muni.pa165.mappers.ApplicationMapper;
+import cz.muni.pa165.service.ApplicationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @author Michal Badin
+ */
+@Service
+@Transactional
+public class ApplicationFacade {
+    private final ApplicationService applicationService;
+    private final ApplicationMapper applicationMapper;
+
+    @Autowired
+    public ApplicationFacade(ApplicationService applicationService, ApplicationMapper applicationMapper) {
+        this.applicationService = applicationService;
+        this.applicationMapper = applicationMapper;
+    }
+
+    @Transactional(readOnly = true)
+    public List<ApplicationDto> getAll() {
+        return applicationMapper.mapToList(applicationService.findAll());
+    }
+
+    public ApplicationDto create(ApplicationCreateDto applicationCreateDto) {
+        return applicationMapper.mapToDto(applicationService.create(applicationMapper.mapFromCreateDto(applicationCreateDto)));
+    }
+
+    @Cacheable(cacheNames = "applications", key = "#id")
+    @Transactional(readOnly = true)
+    public ApplicationDto findById(Long id) {
+        return applicationMapper.mapToDto(applicationService.findById(id));
+    }
+
+    public ApplicationDto setStatus(Long id, ApplicationStatusEnum applicationStatusEnum) {
+        return applicationMapper.mapToDto(applicationService.setStatus(id, applicationStatusEnum));
+    }
+}
diff --git a/application/src/main/java/cz/muni/pa165/mappers/ApplicationMapper.java b/application/src/main/java/cz/muni/pa165/mappers/ApplicationMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5aba3105ac0aa498cb2d7dcfec79c1bcebda9f9
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/mappers/ApplicationMapper.java
@@ -0,0 +1,21 @@
+package cz.muni.pa165.mappers;
+
+import cz.muni.pa165.data.model.Application;
+import cz.muni.pa165.generated.model.ApplicationCreateDto;
+import cz.muni.pa165.generated.model.ApplicationDto;
+import org.mapstruct.Mapper;
+
+import java.util.List;
+
+/**
+ * @author Michal Badin
+ */
+@Mapper(componentModel = "spring")
+public interface ApplicationMapper {
+    ApplicationDto mapToDto(Application application);
+
+    Application mapFromCreateDto(ApplicationCreateDto carCreateDto);
+
+    List<ApplicationDto> mapToList(List<Application> cars);
+}
+
diff --git a/application/src/main/java/cz/muni/pa165/rest/ApplicationController.java b/application/src/main/java/cz/muni/pa165/rest/ApplicationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..edb599279d03bebd9b4d34d571b8d6d0260be7c5
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/rest/ApplicationController.java
@@ -0,0 +1,60 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.data.enums.ApplicationStatusEnum;
+import cz.muni.pa165.facade.ApplicationFacade;
+import cz.muni.pa165.generated.api.ApplicationServiceApiDelegate;
+import cz.muni.pa165.generated.model.ApplicationCreateDto;
+import cz.muni.pa165.generated.model.ApplicationDto;
+import cz.muni.pa165.generated.model.ApplicationStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author Michal Badin
+ */
+
+@Component
+public class ApplicationController implements ApplicationServiceApiDelegate {
+
+    private static final Logger log = LoggerFactory.getLogger(ApplicationController.class);
+    private final ApplicationFacade applicationFacade;
+
+    @Autowired
+    public ApplicationController(ApplicationFacade applicationFacade) {
+        this.applicationFacade = applicationFacade;
+    }
+
+    @Override
+    public ResponseEntity<List<ApplicationDto>> getAllApplications() {
+        log.debug("getAllApplications() called");
+        List<ApplicationDto> applications = applicationFacade.getAll();
+        return new ResponseEntity<>(applications, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ApplicationDto> createApplication(ApplicationCreateDto applicationCreateDto) {
+        log.debug("createApplication() called");
+        ApplicationDto applicationDto = applicationFacade.create(applicationCreateDto);
+        return new ResponseEntity<>(applicationDto, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ApplicationDto> getApplication(Long id) {
+        log.debug("getApplication() called");
+        ApplicationDto applicationDto = applicationFacade.findById(id);
+        return new ResponseEntity<>(applicationDto, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ApplicationDto> setStatus(Long id, ApplicationStatus applicationStatus) {
+        log.debug("setStatus() called");
+        ApplicationDto applicationDto = applicationFacade.setStatus(id, ApplicationStatusEnum.fromValue(applicationStatus.getValue()));
+        return new ResponseEntity<>(applicationDto, HttpStatus.OK);
+    }
+}
diff --git a/application/src/main/java/cz/muni/pa165/rest/DBManagementController.java b/application/src/main/java/cz/muni/pa165/rest/DBManagementController.java
new file mode 100644
index 0000000000000000000000000000000000000000..77aed426da643b134a82c81a46117503ad22fe35
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/rest/DBManagementController.java
@@ -0,0 +1,42 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.generated.api.DbManagementServiceApiDelegate;
+import cz.muni.pa165.generated.model.SuccessfullyProcessed;
+import cz.muni.pa165.service.DBManagementService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Michal Badin
+ */
+@Profile("dev")
+@Component
+public class DBManagementController implements DbManagementServiceApiDelegate {
+    private static final Logger log = LoggerFactory.getLogger(DBManagementController.class);
+
+    private final DBManagementService dbManagementService;
+
+    @Autowired
+    public DBManagementController(DBManagementService dbManagementService) {
+        this.dbManagementService = dbManagementService;
+    }
+
+    @Override
+    public ResponseEntity<SuccessfullyProcessed> seedDB() {
+        log.debug("seedDB() called");
+        dbManagementService.seed();
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<SuccessfullyProcessed> clearDB() {
+        log.debug("clearDB() called");
+        dbManagementService.clear();
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+}
diff --git a/application/src/main/java/cz/muni/pa165/rest/exceptionHandling/ApiError.java b/application/src/main/java/cz/muni/pa165/rest/exceptionHandling/ApiError.java
new file mode 100644
index 0000000000000000000000000000000000000000..abbd4d29bfedbcd1acefd7f71e7f4777a7d1bdd5
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/rest/exceptionHandling/ApiError.java
@@ -0,0 +1,63 @@
+package cz.muni.pa165.rest.exceptionHandling;
+
+import org.springframework.http.HttpStatus;
+
+import java.time.LocalDateTime;
+
+public class ApiError {
+
+    private LocalDateTime timestamp;
+    private HttpStatus status;
+    private String message;
+    private String path;
+
+    public ApiError(LocalDateTime timestamp, HttpStatus status, String message, String path) {
+        this.timestamp = timestamp;
+        this.status = status;
+        this.message = message;
+        this.path = path;
+    }
+
+    public LocalDateTime getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(LocalDateTime timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public HttpStatus getStatus() {
+        return status;
+    }
+
+    public void setStatus(HttpStatus status) {
+        this.status = status;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    @Override
+    public String toString() {
+        return "ApiError{" +
+                "timestamp=" + timestamp +
+                ", status=" + status +
+                ", message='" + message + '\'' +
+                ", path='" + path + '\'' +
+                '}';
+    }
+}
+
diff --git a/application/src/main/java/cz/muni/pa165/rest/exceptionHandling/CustomRestGlobalExceptionHandling.java b/application/src/main/java/cz/muni/pa165/rest/exceptionHandling/CustomRestGlobalExceptionHandling.java
new file mode 100644
index 0000000000000000000000000000000000000000..504e774858988274ba55942f0657142b003b53e7
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/rest/exceptionHandling/CustomRestGlobalExceptionHandling.java
@@ -0,0 +1,58 @@
+package cz.muni.pa165.rest.exceptionHandling;
+
+/**
+ * @author Michal Badin
+ */
+
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.util.UrlPathHelper;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+
+@RestControllerAdvice
+public class CustomRestGlobalExceptionHandling {
+
+    private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper();
+
+    @ExceptionHandler({ResourceNotFoundException.class})
+    public ResponseEntity<ApiError> handleResourceNotFound(
+            final ResourceNotFoundException ex, final HttpServletRequest request) {
+        final ApiError apiError = new ApiError(
+                LocalDateTime.now(Clock.systemUTC()),
+                HttpStatus.NOT_FOUND,
+                ex.getLocalizedMessage(),
+                URL_PATH_HELPER.getRequestUri(request));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle all the exceptions not matched by above-mentioned definitions.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @return the response entity
+     */
+    @ExceptionHandler({Exception.class})
+    public ResponseEntity<ApiError> handleAll(final Exception ex, HttpServletRequest request) {
+        final ApiError apiError = new ApiError(
+                LocalDateTime.now(Clock.systemUTC()),
+                HttpStatus.INTERNAL_SERVER_ERROR,
+                getInitialException(ex).getLocalizedMessage(),
+                URL_PATH_HELPER.getRequestUri(request));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    private Exception getInitialException(Exception ex) {
+        while (ex.getCause() != null) {
+            ex = (Exception) ex.getCause();
+        }
+        return ex;
+    }
+}
diff --git a/application/src/main/java/cz/muni/pa165/service/ApplicationService.java b/application/src/main/java/cz/muni/pa165/service/ApplicationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfdd2fdcc25e1ae16f59c7e49fe21e70a1942c0a
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/service/ApplicationService.java
@@ -0,0 +1,112 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.enums.ApplicationStatusEnum;
+import cz.muni.pa165.data.model.Application;
+import cz.muni.pa165.data.repository.ApplicationRepository;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.Resource;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClientException;
+
+import java.time.LocalDate;
+import java.util.*;
+
+/**
+ * @author Michal Badin
+ */
+@Service
+public class ApplicationService {
+    private static final Logger log = LoggerFactory.getLogger(ApplicationService.class);
+    private static final String NOTIFICATION_MODULE_DOCKER = "http://notification:8083";
+    private static final String NOTIFICATION_MODULE_LOCALHOST = "http://localhost:8083";
+    private static final String NOTIFICATION_MODULE_URI_NEW = "/notification/application/new";
+    private static final String NOTIFICATION_MODULE_URI_STATUS = "/notification/application/status";
+    @Value("${notification.receivers}")
+    private final List<String> notificationReceivers = new ArrayList<>();
+    private final ApplicationRepository applicationRepository;
+    private final WebClient webClient;
+    private String NOTIFICATION_MODULE_URL;
+
+    @Autowired
+    public ApplicationService(ApplicationRepository applicationRepository, WebClient.Builder webClientBuilder) {
+        this.applicationRepository = applicationRepository;
+
+        //if running in docker, modules cannot communicate through localhost
+        if (System.getenv("DOCKER") != null) {
+            NOTIFICATION_MODULE_URL = NOTIFICATION_MODULE_DOCKER;
+        } else {
+            NOTIFICATION_MODULE_URL = NOTIFICATION_MODULE_LOCALHOST;
+        }
+        this.webClient = webClientBuilder.baseUrl(NOTIFICATION_MODULE_URL).build();
+    }
+
+    @Transactional(readOnly = true)
+    public List<Application> findAll() {
+        return applicationRepository.findAll();
+    }
+
+    public Application create(Application application) {
+        application.setFillingOutDate(LocalDate.now());
+        application.setStatus(ApplicationStatusEnum.PENDING);
+
+        var savedApplication = applicationRepository.save(application);
+
+        try {
+            sendPostRequest(savedApplication, NOTIFICATION_MODULE_URI_NEW, notificationReceivers);
+        } catch (WebClientException e) {
+            log.debug(String.format("The notification module is not reachable on the URL: %s, exception %s", NOTIFICATION_MODULE_URL + NOTIFICATION_MODULE_URI_NEW, e));
+        }
+
+        return savedApplication;
+    }
+
+    @Transactional(readOnly = true)
+    public Application findById(Long id) {
+        return applicationRepository.findById(id)
+                .orElseThrow(() ->
+                        new ResourceNotFoundException(String.format("%s with id %s was not found", Application.class, id)));
+    }
+
+    public Application setStatus(Long id, ApplicationStatusEnum applicationStatusEnum) {
+        Optional<Application> applicationFromDb = applicationRepository.findById(id);
+        if (applicationFromDb.isEmpty()) {
+            throw new ResourceNotFoundException(String.format("%s with id %s was not found", Application.class, id));
+        }
+
+        applicationFromDb.get().setStatus(applicationStatusEnum);
+
+        var savedApplication = applicationRepository.save(applicationFromDb.get());
+
+        if (savedApplication.getStatus() != ApplicationStatusEnum.PENDING) {
+            try {
+                sendPostRequest(savedApplication, NOTIFICATION_MODULE_URI_STATUS, List.of(savedApplication.getEmail()));
+            } catch (WebClientException e) {
+                log.debug(String.format("The notification module is not reachable on the URL: %s, exception %s", NOTIFICATION_MODULE_URL + NOTIFICATION_MODULE_URI_STATUS, e));
+            }
+        }
+
+        return savedApplication;
+    }
+
+    public void sendPostRequest(Application application, String uri, List<String> receivers) throws RestClientException {
+        Map<String, Object> notification = new HashMap<>();
+        notification.put("application", application);
+        notification.put("receivers", receivers);
+
+        webClient.post()
+                .uri(uri)
+                .contentType(MediaType.APPLICATION_JSON)
+                .bodyValue(notification)
+                .retrieve()
+                .bodyToMono(Resource.class)
+                .block();
+    }
+}
diff --git a/application/src/main/java/cz/muni/pa165/service/DBManagementService.java b/application/src/main/java/cz/muni/pa165/service/DBManagementService.java
new file mode 100644
index 0000000000000000000000000000000000000000..e89adc428f29049ebe35d920a0ed3d556ff2caa0
--- /dev/null
+++ b/application/src/main/java/cz/muni/pa165/service/DBManagementService.java
@@ -0,0 +1,65 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.enums.ApplicationStatusEnum;
+import cz.muni.pa165.data.model.Application;
+import cz.muni.pa165.data.repository.ApplicationRepository;
+import jakarta.annotation.PostConstruct;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+
+/**
+ * @author Michal Badin
+ */
+@Service
+@Profile("dev")
+public class DBManagementService {
+    private final ApplicationRepository applicationRepository;
+
+    @Autowired
+    public DBManagementService(ApplicationRepository applicationRepository) {
+        this.applicationRepository = applicationRepository;
+    }
+
+    @Transactional
+    @PostConstruct
+    public void seed() {
+        saveApplication(ApplicationStatusEnum.PENDING,
+                "Marek",
+                "Mrkvicka",
+                "marek.mrkvicka@example.com",
+                LocalDate.of(1999, 3, 8),
+                LocalDate.now());
+        saveApplication(ApplicationStatusEnum.ACCEPTED,
+                "Anuja",
+                "Ankska",
+                "ankska.anuja@example.com",
+                LocalDate.of(2000, 1, 30),
+                LocalDate.of(2010, 7, 18));
+        saveApplication(ApplicationStatusEnum.ACCEPTED,
+                "Xiao",
+                "Chen",
+                "xiao.chen@example.com",
+                LocalDate.of(1995, 7, 29),
+                LocalDate.of(2012, 10, 9));
+        saveApplication(ApplicationStatusEnum.REJECTED,
+                "Gertruda",
+                "Fialkova",
+                "gertruda.fialkova@example.com",
+                LocalDate.of(1965, 6, 2),
+                LocalDate.of(2023, 4, 26));
+    }
+
+    private void saveApplication(ApplicationStatusEnum status, String name, String surname, String email, LocalDate birthday, LocalDate fillingOutDate) {
+        Application application = new Application(status, name, surname, birthday, email, fillingOutDate);
+        applicationRepository.save(application);
+    }
+
+    @Transactional
+    public void clear() {
+        applicationRepository.deleteAll();
+    }
+}
diff --git a/application/src/main/resources/application.properties b/application/src/main/resources/application.properties
new file mode 100644
index 0000000000000000000000000000000000000000..a41e2a36375436609b3fed5d065ae2da58843478
--- /dev/null
+++ b/application/src/main/resources/application.properties
@@ -0,0 +1,22 @@
+logging.level.root=info
+logging.level.cz.muni=debug
+server.port=8081
+spring.jpa.open-in-view=false
+spring.datasource.url=jdbc:h2:mem:formula-application;MODE=PostgreSQL
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=formulkyApplication
+spring.datasource.password=123456
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.jpa.show-sql=true
+spring.jackson.property-naming-strategy=SNAKE_CASE
+spring.cache.type=NONE
+spring.profiles.active=dev
+appconfig.enablecache=false
+
+management.endpoints.web.exposure.include=health,metrics,loggers,beans,prometheus
+management.endpoint.health.show-details=always
+management.endpoint.health.show-components=always
+management.endpoint.health.probes.enabled=true
+
+#notification receivers
+notification.receivers=formula.team.management@gmail.com
\ No newline at end of file
diff --git a/application/src/test/java/cz/muni/pa165/ApplicationIT.java b/application/src/test/java/cz/muni/pa165/ApplicationIT.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea447845786e1d3b105a1b551683339e63a64526
--- /dev/null
+++ b/application/src/test/java/cz/muni/pa165/ApplicationIT.java
@@ -0,0 +1,52 @@
+package cz.muni.pa165;
+
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Integration tests. Run by "mvn verify / mvn test / mvn clean install".
+ * No need to test all the rest controllers logic here, just test that the endpoints are working
+ */
+@SpringBootTest
+@AutoConfigureMockMvc
+@ActiveProfiles("test")
+class ApplicationIT {
+
+    private static final Logger log = LoggerFactory.getLogger(ApplicationIT.class);
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @Test
+    void testCreateApplication() throws Exception {
+        log.info("testApplicationModuleResponse() running");
+
+        String testRequestContent = "{\"name\":\"John\",\"surname\":\"Doe\",\"birthday\":\"2000-01-01\",\"email\":\"john.doe@aaa.com\"}";
+
+        String response = mockMvc.perform(
+                        post("/application")
+                                .accept(MediaType.APPLICATION_JSON)
+                                .contentType(MediaType.APPLICATION_JSON)
+                                .content(testRequestContent))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+        log.info("response: {}", response);
+
+        String expectedResponse = "{\"id\":1,\"name\":\"John\",\"surname\":\"Doe\",\"birthday\":\"2000-01-01\",\"email\":\"john.doe@aaa.com\",\"fillingOutDate\":\"" + LocalDate.now() + "\",\"status\":\"pending\"}";
+
+        assertEquals(expectedResponse, response);
+    }
+}
\ No newline at end of file
diff --git a/application/src/test/java/cz/muni/pa165/rest/ApplicationControllerTest.java b/application/src/test/java/cz/muni/pa165/rest/ApplicationControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..31cae144aa52bfdeb928ce45433005d309728be8
--- /dev/null
+++ b/application/src/test/java/cz/muni/pa165/rest/ApplicationControllerTest.java
@@ -0,0 +1,35 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.data.enums.ApplicationStatusEnum;
+import cz.muni.pa165.facade.ApplicationFacade;
+import cz.muni.pa165.generated.model.ApplicationStatus;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ActiveProfiles;
+
+class ApplicationControllerTest {
+
+    private static final Logger log = LoggerFactory.getLogger(ApplicationControllerTest.class);
+    private final ApplicationFacade mockFacade = Mockito.mock(ApplicationFacade.class);
+    private final ApplicationController carComponentController = new ApplicationController(mockFacade);
+
+    @Test
+    void testGetApplication() {
+        log.debug("starting testGetApplication()");
+
+        carComponentController.getApplication(1L);
+
+        Mockito.verify(mockFacade, Mockito.times(1)).findById(1L);
+    }
+
+    @Test
+    void testSetApplicationStatus() {
+        log.debug("starting testGetCarComponentsByType()");
+
+        carComponentController.setStatus(1L, ApplicationStatus.ACCEPTED);
+
+        Mockito.verify(mockFacade, Mockito.times(1)).setStatus(1L, ApplicationStatusEnum.ACCEPTED);
+    }
+}
\ No newline at end of file
diff --git a/application/src/test/java/cz/muni/pa165/service/ApplicationServiceTest.java b/application/src/test/java/cz/muni/pa165/service/ApplicationServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5715cd0f1c90c45a691a38f1467b0f082d30a6d7
--- /dev/null
+++ b/application/src/test/java/cz/muni/pa165/service/ApplicationServiceTest.java
@@ -0,0 +1,57 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.enums.ApplicationStatusEnum;
+import cz.muni.pa165.data.model.Application;
+import cz.muni.pa165.data.repository.ApplicationRepository;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.time.LocalDate;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.when;
+
+class ApplicationServiceTest {
+    ApplicationRepository applicationRepository = Mockito.mock(ApplicationRepository.class);
+    WebClient.Builder webClientBuilder = WebClient.builder();
+    ApplicationService service = new ApplicationService(applicationRepository, webClientBuilder);
+    Application application = new Application(ApplicationStatusEnum.ACCEPTED, "John", "Doe",
+            LocalDate.now().minusYears(20), "john.doe@example.com", LocalDate.now());
+
+
+    @Test
+    void create() {
+        when(applicationRepository.save(application)).thenReturn(application);
+
+        Application created = service.create(application);
+
+        assertEquals(application, created);
+        assertEquals(ApplicationStatusEnum.PENDING, created.getStatus());
+    }
+
+    @Test
+    void setStatus() {
+        Application expected = new Application(ApplicationStatusEnum.REJECTED, "John", "Doe",
+                LocalDate.now().minusYears(20), "john.doe@example.com", LocalDate.now());
+        when(applicationRepository.save(application)).thenReturn(expected);
+        when(applicationRepository.findById(application.getId())).thenReturn(java.util.Optional.ofNullable(application));
+
+        Application actual = service.setStatus(application.getId(), ApplicationStatusEnum.REJECTED);
+
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    void setStatusUnknownApplication() {
+        when(applicationRepository.findById(application.getId())).thenReturn(Optional.empty());
+
+        assertThrows(ResourceNotFoundException.class,
+                () -> service.setStatus(application.getId(), ApplicationStatusEnum.REJECTED));
+    }
+}
\ No newline at end of file
diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties
new file mode 100644
index 0000000000000000000000000000000000000000..837dfbd2700c1f3f507eda6ef46996a3fdc33294
--- /dev/null
+++ b/application/src/test/resources/application-test.properties
@@ -0,0 +1,12 @@
+spring.datasource.url=jdbc:h2:mem:testApplicationdb;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;
+spring.datasource.username=formulky-application-test
+spring.datasource.password=
+spring.datasource.driverClassName=org.h2.Driver
+spring.jpa.hibernate.ddl-auto=create-drop
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
+spring.jpa.properties.hibernate.generate_statistics=true
+spring.jpa.properties.hibernate.format_sql=true
+spring.jpa.properties.hibernate.show_sql=false
+spring.h2.console.enabled=true
+spring.cache.type=NONE
+appconfig.enablecache=false
\ No newline at end of file
diff --git a/application/src/test/resources/logback.xml b/application/src/test/resources/logback.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6690d6a5e55b0d38783a9ddef0471132ce6ddbe9
--- /dev/null
+++ b/application/src/test/resources/logback.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%d %5p %40.40c:%4L - %m%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="info">
+        <appender-ref ref="console"/>
+    </root>
+    <!-- TODO remove solution-->
+    <logger name="org.hibernate.SQL" level="DEBUG"/>
+
+</configuration>
\ No newline at end of file
diff --git a/compose.yaml b/compose.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e70e897a593e4ee8cf3aa3a1b0f468c2c27827a0
--- /dev/null
+++ b/compose.yaml
@@ -0,0 +1,56 @@
+services:
+  application:
+    build:
+      context: .
+      target: pa165-formula-team-management-application
+    container_name: application
+    ports:
+      - "8081:8081"
+    environment:
+      - DOCKER=1
+
+  core:
+    build:
+      context: .
+      target: pa165-formula-team-management-core
+    container_name: core
+    ports:
+      - "8090:8090"
+    environment:
+      - DOCKER=1
+
+  notification:
+    build:
+      context: .
+      target: pa165-formula-team-management-notification
+    container_name: notification
+    ports:
+      - "8083:8083"
+
+  visualization:
+    build:
+      context: .
+      target: pa165-formula-team-management-visualization
+    container_name: visualization
+    volumes:
+      - ./visualization/output-data:/output-data
+    ports:
+      - "8082:8082"
+
+  prometheus:
+    image: prom/prometheus:v2.43.0
+    container_name: prometheus
+    volumes:
+      - ./prometheus.yml:/etc/prometheus/prometheus.yml
+    command: --config.file=/etc/prometheus/prometheus.yml
+    ports:
+      - "9090:9090"
+
+  grafana:
+    image: grafana/grafana:9.1.7
+    container_name: grafana
+    volumes:
+      - ./datasource.yml:/etc/grafana/provisioning/datasources/datasource.yml
+      - ./dashboards:/etc/grafana/provisioning/dashboards
+    ports:
+      - "3000:3000"
diff --git a/core/openapi.yaml b/core/openapi.yaml
index 3e74c606b5ef4f970acf1d6e013d4170f9b1b39b..ccfc52137ba8a6890be4d9d831ef2a4e57dfa938 100644
--- a/core/openapi.yaml
+++ b/core/openapi.yaml
@@ -5,30 +5,79 @@ info:
     Hand-made OpenAPI document.
   version: 1.0.0
 servers:
-  - url: "http://localhost:8080"
+  - url: "http://localhost:8090"
 tags:
-  - name: ComponentService
+  - name: CarComponentService
   - name: CarService
+  - name: DriverService
+  - name: EngineerService
 
 components:
   schemas:
-    Component:
+    CarComponentType:
+      type: string
+      description: Type of a component
+      enum:
+        - CHASSIS
+        - ENGINE
+        - FRONTWING
+        - SUSPENSION
+
+    CarComponentDto:
       type: object
       title: Component
       description: Component of a car
       required:
         - id
-        - weight
+        - componentType
+        - information
       properties:
         id:
           type: integer
-          description: id of the component
+          format: int64
+          description: Id of the component
           example: 1
+        componentType:
+          $ref: '#/components/schemas/CarComponentType'
         weight:
           type: number
-          description: color of the component
+          description: Weight of the component
           example: 50.5
-    Car:
+        information:
+          type: string
+          description: Specific information about component
+          example: lightweight front wing v2 (black)
+          maxLength: 100
+    CarComponentCreateDto:
+      type: object
+      properties:
+        componentType:
+          $ref: '#/components/schemas/CarComponentType'
+        weight:
+          type: number
+          description: Component's weight
+        information:
+          type: string
+          description: Specific information about component
+          maxLength: 100
+      required:
+        - componentType
+        - weight
+        - information
+    CarComponentUpdateDto:
+      type: object
+      properties:
+        componentType:
+          $ref: '#/components/schemas/CarComponentType'
+        weight:
+          type: number
+          description: car component's weight
+        information:
+          type: string
+          description: Specific information about component
+          maxLength: 100
+
+    CarDto:
       type: object
       title: Car
       description: Formula
@@ -37,80 +86,918 @@ components:
       properties:
         id:
           type: integer
+          format: int64
           description: Id of the car
           example: 1
-        color:
-          type: string
-          description: Color of the car
-          example: 'red'
         components:
           type: array
           description: List of components in the car
           items:
-            $ref: '#/components/schemas/Component'
+            $ref: '#/components/schemas/CarComponentDto'
+        driver:
+          $ref: '#/components/schemas/DriverDto'
+
+    Characteristic:
+      type: string
+      enum:
+        - aggressiveness
+        - driving on the wet
+        - react quickly
+        - remain completely focused
+        - adaptability to the environment
+        - strong neck
+        - ability to interact with the team
+        - efficient cardiovascular systems
+        - excellent when overtaking
+        - excellent starter
+        - excellent in the corners
+        - excellent in straight lines
+
+    DriverDto:
+      type: object
+      title: Driver
+      description: Driver of a car
+      required:
+        - id
+        - name
+        - surname
+        - nationality
+        - characteristics
+      properties:
+        id:
+          type: integer
+          format: int64
+          description: Id of the driver
+          example: 1
+        name:
+          type: string
+          description: Name of the driver
+          example: Max
+          maxLength: 35
+        surname:
+          type: string
+          description: Surname of the driver
+          example: Verstappen
+          maxLength: 35
+        height:
+          type: integer
+          description: Height in cm
+          example: 170
+        birthday:
+          type: string
+          description: Date of birth
+          format: date
+          example: 1997-09-30
+        nationality:
+          type: string
+          description: Nationality of the driver
+          example: Dutch
+          maxLength: 20
+        characteristics:
+          type: array
+          description: Set of driver's characteristics
+          items:
+            $ref: '#/components/schemas/Characteristic'
+    DriverUpdateDto:
+      type: object
+      properties:
+        name:
+          type: string
+          description: Name of the driver
+          maxLength: 35
+        surname:
+          type: string
+          description: Name of the driver
+          maxLength: 35
+        nationality:
+          type: string
+          description: nationality of the driver
+          maxLength: 20
+        height:
+          type: integer
+          description: Height in cm
+        characteristics:
+          type: array
+          description: Set of driver's characteristics
+          items:
+            $ref: '#/components/schemas/Characteristic'
+    DriverCreateDto:
+      type: object
+      properties:
+        name:
+          type: string
+          description: Name of the driver
+          maxLength: 35
+        surname:
+          type: string
+          description: Name of the driver
+          maxLength: 35
+        nationality:
+          type: string
+          description: Nationality of the driver
+          maxLength: 20
+        height:
+          type: integer
+          description: Height in cm
+        birthday:
+          type: string
+          description: Date of birth
+          format: date
+          example: 1997-09-30
+        characteristics:
+          type: array
+          description: Set of driver's characteristics
+          items:
+            $ref: '#/components/schemas/Characteristic'
+      required:
+        - name
+        - surname
+        - nationality
+        - characteristics
+
+    EngineerDto:
+      type: object
+      title: Engineer
+      description: Engineer from a department
+      required:
+        - name
+        - surname
+      properties:
+        id:
+          type: integer
+          format: int64
+          description: ID of the engineer
+          example: 1
+        name:
+          type: string
+          description: Name of the engineer
+          example: John
+          maxLength: 35
+        surname:
+          type: string
+          description: Surname of the engineer
+          example: Doe
+          maxLength: 35
+    EngineerCreateDto:
+      type: object
+      title: Engineer
+      description: Engineer from a department
+      required:
+        - name
+        - surname
+      properties:
+        name:
+          type: string
+          description: Name of the engineer
+          example: John
+          maxLength: 35
+        surname:
+          type: string
+          description: Surname of the engineer
+          example: Doe
+          maxLength: 35
+
+    DepartmentDto:
+      type: object
+      title: Department
+      description: Department
+      properties:
+        id:
+          type: integer
+          format: int64
+          description: ID of the department
+          example: 1
+        engineers:
+          type: array
+          description: List of engineers belonging to the department
+          items:
+            $ref: '#/components/schemas/EngineerDto'
+        specialization:
+          type: string
+          example: "Aerodynamics"
+          maxLength: 100
+    DepartmentUpdateDto:
+      type: object
+      properties:
+        specialization:
+          type: string
+          example: "Aerodynamics"
+          description: New specialization
+          maxLength: 100
+      required:
+        - specialization
+    DepartmentCreateDto:
+      type: object
+      properties:
+        specialization:
+          type: string
+          example: "Aerodynamics"
+          maxLength: 100
+      required:
+        - specialization
+    Pageable:
+      type: object
+      properties:
+        page:
+          type: integer
+          example: 0
+        size:
+          type: integer
+          example: 10
+    CarDtoPage:
+      type: object
+      properties:
+        content:
+          type: array
+          items:
+            $ref: '#/components/schemas/CarDto'
+        pageable:
+          $ref: '#/components/schemas/Pageable'
+        totalPages:
+          type: integer
+          example: 3
+        totalElements:
+          type: integer
+          example: 25
+        isLast:
+          type: boolean
+          example: false
+        size:
+          type: integer
+          example: 10
+        number:
+          type: integer
+          example: 0
+        sort:
+          type: object
+          example:
+            sorted: false
+            unsorted: true
+            empty: true
+
+  responses:
+    NotFound:
+      description: The specified resource was not found
+      content:
+        application/json:
+          schema:
+            type: object
+            title: NotFound
+            properties:
+              message:
+                type: string
+    Unauthorized:
+      description: Unauthorized access
+      content:
+        application/json:
+          schema:
+            type: object
+            title: Unauthorized
+            properties:
+              message:
+                type: string
+    Unexpected:
+      description: Unexpected error
+      content:
+        application/json:
+          schema:
+            type: object
+            title: Unexpected
+            properties:
+              message:
+                type: string
+    Successful:
+      description: Successfully processed
+      content:
+        application/json:
+          schema:
+            type: object
+            title: Successfully processed
+            properties:
+              message:
+                type: string
 
 paths:
-  /car/get:
+  /driver:
+    post:
+      tags:
+        - DriverService
+      summary: Create a driver
+      operationId: createDriver
+      requestBody:
+        description: Request body for creating a new driver
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/DriverCreateDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/DriverDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    get:
+      tags:
+        - DriverService
+      summary: Returns all drivers
+      operationId: getAllDrivers
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/DriverDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /driver/test:
+    get:
+      tags:
+        - DriverService
+      summary: Returns all test drivers
+      operationId: getAllTestDrivers
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/DriverDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /driver/{id}:
+    get:
+      tags:
+        - DriverService
+      summary: Returns driver with the given id
+      operationId: getDriver
+      parameters:
+        - { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: "Id of the driver" }
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/DriverDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    patch:
+      tags:
+        - DriverService
+      summary: Updates driver with the given id
+      operationId: updateDriver
+      parameters:
+        - in: path
+          name: id
+          schema:
+            type: integer
+            format: int64
+          required: true
+          description: "ID of driver"
+      requestBody:
+        description: Request body for updating driver
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/DriverUpdateDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/DriverDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    delete:
+      tags:
+        - DriverService
+      summary: Deletes driver with the given id
+      operationId: deleteDriver
+      parameters:
+        - { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: "Id of the driver" }
+      responses:
+        "200":
+          description: OK
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+
+  /car:
+    post:
+      tags:
+        - CarService
+      summary: Creates a car
+      operationId: createCar
+      parameters:
+        - { name: componentIds, in: query, schema: { type: array, items: { type: integer, format: int64 } },
+            description: "ids of the components" }
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CarDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    get:
+      tags:
+        - CarService
+      summary: Returns all cars
+      operationId: getAllCars
+      parameters:
+        - { name: page, in: query, required: true, schema: { type: integer, default: 0 }, description: "page number" }
+        - { name: size, in: query, required: true, schema: { type: integer, default: 20 }, description: "size of the page" }
+      responses:
+        '200':
+          description: Successful response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CarDtoPage'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /car/{id}:
     get:
       tags:
         - CarService
       summary: Returns car with the given id
       operationId: getCar
       parameters:
-        - { name: id, in: query, required: true, schema: { type: integer }, description: "id of the car" }
+        - { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: "id of the car" }
       responses:
         "200":
           description: OK
           content:
             application/json:
               schema:
-                $ref: '#/components/schemas/Car'
+                $ref: '#/components/schemas/CarDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    patch:
+      tags:
+        - CarService
+      summary: Updates car with the given id
+      operationId: updateCar
+      parameters:
+        - { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: "id of the car" }
+        - { name: componentIds, in: query, schema: { type: array, items: { type: integer, format: int64 } },
+            description: "ids of the components" }
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CarDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    delete:
+      tags:
+        - CarService
+      summary: Deletes car with the given id
+      operationId: deleteCar
+      parameters:
+        - { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: "id of the car" }
+      responses:
+        "200":
+          description: OK
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /car/driver:
+    put:
+      tags:
+        - CarService
+      summary: Set driver to the car with id
+      operationId: setDriver
+      parameters:
+        - { name: carId, in: query, required: true, schema: { type: integer, format: int64 }, description: "id of the car" }
+        - { name: driverId, in: query, required: true, schema: { type: integer, format: int64 }, description: "id of the driver" }
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CarDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    delete:
+      tags:
+        - CarService
+      summary: Remove driver from the car with id
+      operationId: removeDriver
+      parameters:
+        - { name: carId, in: query, required: true, schema: { type: integer, format: int64 }, description: "id of the car" }
+      responses:
+        "200":
+          description: OK
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+
+  /carComponent/{id}:
+    get:
+      tags:
+        - CarComponentService
+      summary: Returns component with the given id
+      operationId: getCarComponent
+      parameters:
+        - { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: "id of the component" }
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CarComponentDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    delete:
+      tags:
+        - CarComponentService
+      summary: Deletes car component with the given id
+      operationId: deleteCarComponent
+      parameters:
+        - { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: "id of the car" }
+      responses:
+        "200":
+          description: OK
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    patch:
+      tags:
+        - CarComponentService
+      summary: Updates car component with the given id
+      operationId: updateCarComponent
+      parameters:
+        - { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: "id of the component" }
+      requestBody:
+        description: Request body for updating car component
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/CarComponentUpdateDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/CarComponentDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
 
-  /car/create:
+  /carComponent:
+    get:
+      tags:
+        - CarComponentService
+      summary: Returns all components
+      operationId: getAllCarComponents
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/CarComponentDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
     post:
       tags:
-        - CarService
-      summary: creates a car
-      operationId: createCar
+        - CarComponentService
+      summary: Creates a car component
+      operationId: createCarComponent
       requestBody:
-        description: Request body for creating a new car
+        description: Request body for creating a new car component
         required: true
         content:
           application/json:
             schema:
-              type: object
-              properties:
-                color:
-                  type: string
-                  description: The color of the car
-                  example: "red"
-                  minLength: 1
-                components:
-                  type: array
-                  description: List of components in the car
-                  items:
-                    $ref: '#/components/schemas/Component'
-              required:
-                - color
+              $ref: '#/components/schemas/CarComponentCreateDto'
       responses:
         "200":
           description: OK
           content:
             application/json:
               schema:
-                $ref: '#/components/schemas/Car'
-  /component/get:
+                $ref: '#/components/schemas/CarComponentDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+
+  /carComponent/type:
     get:
       tags:
-        - ComponentService
-      summary: Returns component with the given id
-      operationId: getComponent
+        - CarComponentService
+      summary: Returns all components of specified type
+      operationId: getCarComponentsByType
+      parameters:
+        - { name: componentType, in: query, required: true, schema: { $ref: '#/components/schemas/CarComponentType' }, description: "type of component" }
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/CarComponentDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+
+  /department/{departmentId}:
+    get:
+      tags:
+        - DepartmentService
+      summary: Returns department with the given ID
+      operationId: getDepartment
+      parameters:
+        - in: path
+          name: departmentId
+          schema:
+            type: integer
+            format: int64
+          required: true
+          description: "ID of department"
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/DepartmentDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    put:
+      tags:
+        - DepartmentService
+      summary: Add new engineer to a department with given id
+      operationId: addEngineer
+      parameters:
+        - in: path
+          name: departmentId
+          schema:
+            type: integer
+            format: int64
+          required: true
+          description: "ID of department"
+        - in: query
+          name: engineerId
+          schema:
+            type: integer
+            format: int64
+          required: true
+          description: "ID of Engineer"
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/DepartmentDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    delete:
+      tags:
+        - DepartmentService
+      summary: Deletes department with the given ID
+      operationId: deleteDepartment
+      parameters:
+        - in: path
+          name: departmentId
+          schema:
+            type: integer
+            format: int64
+          required: true
+          description: "ID of department"
+      responses:
+        "200":
+          description: Department deleted successfully
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    patch:
+      tags:
+        - DepartmentService
+      summary: Update department with the given ID with given specialization
+      operationId: updateSpecialization
+      parameters:
+        - in: path
+          name: departmentId
+          schema:
+            type: integer
+            format: int64
+          required: true
+          description: "ID of department"
+      requestBody:
+        description: Request body for updating Specialization of department
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/DepartmentUpdateDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/DepartmentDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /department:
+    get:
+      tags:
+        - DepartmentService
+      summary: Returns all departments
+      operationId: getDepartments
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/DepartmentDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    post:
+      tags:
+        - DepartmentService
+      summary: Creates new department
+      operationId: createDepartment
+      requestBody:
+        description: Request body for creating a new department
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/DepartmentCreateDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/DepartmentDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+
+  /engineer:
+    get:
+      tags:
+        - EngineerService
+      summary: Returns all engineers
+      operationId: getEngineers
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/EngineerDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    post:
+      tags:
+        - EngineerService
+      summary: Creates new engineer
+      operationId: createEngineer
+      requestBody:
+        description: Request body for creating a new engineer
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/EngineerCreateDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/EngineerDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /engineer/{id}:
+    get:
+      tags:
+        - EngineerService
+      summary: Returns engineer with the given ID
+      operationId: getEngineer
       parameters:
-        - { name: id, in: query, required: true, schema: { type: integer }, description: "id of the component" }
+        - in: path
+          name: id
+          schema:
+            type: integer
+            format: int64
+          required: true
+          description: "ID of engineer"
       responses:
         "200":
           description: OK
           content:
             application/json:
               schema:
-                $ref: '#/components/schemas/Component'
+                $ref: '#/components/schemas/EngineerDto'
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+    delete:
+      tags:
+        - EngineerService
+      summary: Deletes engineer with the given ID
+      operationId: deleteEngineer
+      parameters:
+        - in: path
+          name: id
+          schema:
+            type: integer
+            format: int64
+          required: true
+          description: "ID of engineer"
+      responses:
+        "200":
+          description: OK
+        "404":
+          $ref: '#/components/responses/NotFound'
+        default:
+          $ref: '#/components/responses/Unexpected'
+
+  /seed:
+    post:
+      tags:
+        - DBManagementService
+      summary: Seed DB with data
+      operationId: seedDB
+      responses:
+        "200":
+          $ref: '#/components/responses/Successful'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /clear:
+    post:
+      tags:
+        - DBManagementService
+      summary: Clear DB
+      operationId: clearDB
+      responses:
+        "200":
+          $ref: '#/components/responses/Successful'
+        default:
+          $ref: '#/components/responses/Unexpected'
diff --git a/core/pom.xml b/core/pom.xml
index 37801edb6b2f79d4d2659c29338c28318d0f745a..fc6b5b9a2f8d3ade94bb570d218af86c1a97a46d 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -14,13 +14,13 @@
     <!-- this module definition -->
     <artifactId>core</artifactId>
     <packaging>jar</packaging>
-    <name>Generated core</name>
+    <name>Core module</name>
     <description>Core generated using OpenAPI Generator "java"</description>
 
     <build>
         <defaultGoal>spring-boot:run</defaultGoal>
         <!-- name of executable JAR file -->
-        <finalName>core_generated</finalName>
+        <finalName>core_module</finalName>
 
         <plugins>
             <plugin>
@@ -38,7 +38,7 @@
                             <modelPackage>cz.muni.pa165.generated.core.model</modelPackage>
                             <!-- https://openapi-generator.tech/docs/generators/spring -->
                             <configOptions>
-                                <basePackage>cz.muni.pa165.generated.core</basePackage>
+                                <basePackage>cz.muni.pa165</basePackage>
                                 <configPackage>cz.muni.pa165.generated.core.config</configPackage>
                                 <useSpringBoot3>true</useSpringBoot3>
                                 <useTags>true</useTags>
@@ -61,10 +61,19 @@
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-failsafe-plugin</artifactId>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M7</version>
+            </plugin>
         </plugins>
     </build>
 
     <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
@@ -93,19 +102,82 @@
             <groupId>javax.validation</groupId>
             <artifactId>validation-api</artifactId>
         </dependency>
-
-        <!-- for including Swagger UI -->
         <dependency>
             <groupId>org.springdoc</groupId>
             <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
         </dependency>
-
-        <!-- for testing -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-cache</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <version>2.7.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-processor</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-oauth2-client</artifactId>
+        </dependency>
+
+        <!--		Actuator dependency-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+
+        <!--		Prometheus dependency -->
+        <dependency>
+            <groupId>io.micrometer</groupId>
+            <artifactId>micrometer-registry-prometheus</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webflux</artifactId>
+            <version>6.0.6</version>
+        </dependency>
 
     </dependencies>
 
diff --git a/core/src/main/java/cz/muni/pa165/actuator/CustomInfoContributor.java b/core/src/main/java/cz/muni/pa165/actuator/CustomInfoContributor.java
new file mode 100644
index 0000000000000000000000000000000000000000..7977bb36c3d78157df8d70cf04855c2a70a627fd
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/actuator/CustomInfoContributor.java
@@ -0,0 +1,13 @@
+package cz.muni.pa165.actuator;
+
+import org.springframework.boot.actuate.info.Info;
+import org.springframework.boot.actuate.info.InfoContributor;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CustomInfoContributor implements InfoContributor {
+    @Override
+    public void contribute(Info.Builder builder) {
+        builder.withDetail("course", "PA165").withDetail("module", "core").build();
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/config/SecurityConfig.java b/core/src/main/java/cz/muni/pa165/config/SecurityConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..5935f7624b6aa3dbd56b5473b5636c64fd886efe
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/config/SecurityConfig.java
@@ -0,0 +1,94 @@
+package cz.muni.pa165.config;
+
+import io.swagger.v3.oas.models.security.*;
+import org.springdoc.core.customizers.OpenApiCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+
+
+@Configuration
+@EnableWebSecurity
+@EnableWebMvc
+public class SecurityConfig {
+
+    @Bean
+    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+        http
+                .authorizeHttpRequests(x -> x
+                        .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/seed", "/clear").permitAll()
+                        .requestMatchers(HttpMethod.POST,"/carComponent").hasAuthority("SCOPE_test_1")
+                        .requestMatchers(HttpMethod.GET, "/carComponent").hasAnyAuthority("SCOPE_test_5", "SCOPE_test_1")
+                        .requestMatchers("/carComponent/**").hasAnyAuthority("SCOPE_test_5", "SCOPE_test_1")
+                        .requestMatchers("/car", "/car/**", "/driver/**", "/driver").hasAuthority("SCOPE_test_5")
+                        .requestMatchers("/engineer", "/engineer/**", "/department", "/department/**").hasAuthority("SCOPE_test_5")
+                        .anyRequest().denyAll()
+                )
+                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken)
+        ;
+        return http.build();
+    }
+
+    //settings for swaggerUI, all of this should be in openapi.yaml, but it doesn't work when put there
+    @Bean
+    public OpenApiCustomizer openAPICustomizer() {
+        return openApi -> {
+            openApi.getComponents()
+                    .addSecuritySchemes("OAuth2",
+                            new SecurityScheme()
+                                    .type(SecurityScheme.Type.OAUTH2)
+                                    .description("get access token with Authorization Code Grant")
+                                    .flows(new OAuthFlows()
+                                            .authorizationCode(new OAuthFlow()
+                                                    .authorizationUrl("https://oidc.muni.cz/oidc/authorize")
+                                                    .tokenUrl("https://oidc.muni.cz/oidc/token")
+                                                    .scopes(new Scopes()
+                                                            .addString("test_5", "manager scope")
+                                                            .addString("test_1", "engineer scope")
+                                                    )
+                                            )
+                                    )
+                    );
+
+            var managerScopeRequirement = new SecurityRequirement().addList("OAuth2", "test_5");
+            var engineerScopeRequirement = new SecurityRequirement().addList("OAuth2", "test_1");
+
+            openApi.getPaths().get("/car").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/car").getPost().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/car/driver").getPut().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/car/driver").getDelete().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/car/{id}").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/car/{id}").getDelete().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/car/{id}").getPatch().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/driver").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/driver").getPost().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/driver/{id}").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/driver/{id}").getDelete().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/driver/{id}").getPatch().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/driver/test").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/carComponent").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/carComponent/{id}").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/carComponent/{id}").getDelete().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/carComponent/{id}").getPatch().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/carComponent/type").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/department/{departmentId}").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/department/{departmentId}").getPut().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/department/{departmentId}").getDelete().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/department/{departmentId}").getPatch().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/department").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/department").getPost().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/engineer").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/engineer").getPost().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/engineer/{id}").getGet().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/engineer/{id}").getDelete().addSecurityItem(managerScopeRequirement);
+            openApi.getPaths().get("/carComponent").getPost().addSecurityItem(engineerScopeRequirement);
+        };
+    }
+
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/cz/muni/pa165/config/ServiceConfig.java b/core/src/main/java/cz/muni/pa165/config/ServiceConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..4af3803cff63a52b4558a83b2d42bcd38b2292e8
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/config/ServiceConfig.java
@@ -0,0 +1,17 @@
+package cz.muni.pa165.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@Configuration
+@ComponentScan
+@EnableTransactionManagement
+public class ServiceConfig {
+    @Bean
+    public WebClient.Builder getWebClientBuilder() {
+        return WebClient.builder();
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/data/enums/CharacteristicsEnum.java b/core/src/main/java/cz/muni/pa165/data/enums/CharacteristicsEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..a17723209fa761bd3c301e9ac07dc9706a23c2f6
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/enums/CharacteristicsEnum.java
@@ -0,0 +1,56 @@
+package cz.muni.pa165.data.enums;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public enum CharacteristicsEnum {
+    AGGRESSIVENESS("aggressiveness"),
+
+    DRIVING_ON_THE_WET("driving on the wet"),
+
+    REACT_QUICKLY("react quickly"),
+
+    REMAIN_COMPLETELY_FOCUSED("remain completely focused"),
+
+    ADAPTABILITY_TO_THE_ENVIRONMENT("adaptability to the environment"),
+
+    STRONG_NECK("strong neck"),
+
+    ABILITY_TO_INTERACT_WITH_THE_TEAM("ability to interact with the team"),
+
+    EFFICIENT_CARDIOVASCULAR_SYSTEMS("efficient cardiovascular systems"),
+
+    EXCELLENT_WHEN_OVERTAKING("excellent when overtaking"),
+
+    EXCELLENT_STARTER("excellent starter"),
+
+    EXCELLENT_IN_THE_CORNERS("excellent in the corners"),
+
+    EXCELLENT_IN_STRAIGHT_LINES("excellent in straight lines");
+
+    private final String value;
+
+    CharacteristicsEnum(String value) {
+        this.value = value;
+    }
+
+    @JsonValue
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(value);
+    }
+
+    @JsonCreator
+    public static CharacteristicsEnum fromValue(String value) {
+        for (CharacteristicsEnum b : CharacteristicsEnum.values()) {
+            if (b.value.equals(value)) {
+                return b;
+            }
+        }
+        throw new IllegalArgumentException("Unexpected value '" + value + "'");
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/data/enums/ComponentTypeEnum.java b/core/src/main/java/cz/muni/pa165/data/enums/ComponentTypeEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..33880ae5acf55902da769bdcdda856bbaaf17903
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/enums/ComponentTypeEnum.java
@@ -0,0 +1,38 @@
+package cz.muni.pa165.data.enums;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public enum ComponentTypeEnum {
+    CHASSIS("CHASSIS"),
+    ENGINE("ENGINE"),
+    FRONTWING("FRONTWING"),
+    SUSPENSION("SUSPENSION");
+
+    private final String value;
+
+    ComponentTypeEnum(String value) {
+        this.value = value;
+    }
+
+    @JsonValue
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(value);
+    }
+
+    @JsonCreator
+    public static ComponentTypeEnum fromValue(String value) {
+        for (ComponentTypeEnum b : ComponentTypeEnum.values()) {
+            if (b.value.equals(value)) {
+                return b;
+            }
+        }
+        throw new IllegalArgumentException("Unexpected value '" + value + "'");
+    }
+}
+
diff --git a/core/src/main/java/cz/muni/pa165/data/model/Car.java b/core/src/main/java/cz/muni/pa165/data/model/Car.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1e9cee840e2a775f7485edc42405a296a184ad2
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/model/Car.java
@@ -0,0 +1,67 @@
+package cz.muni.pa165.data.model;
+
+import com.fasterxml.jackson.annotation.JsonManagedReference;
+import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nullable;
+import jakarta.persistence.*;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+@Entity
+@Table(name = "car")
+public class Car extends DomainObject<Long> implements Serializable {
+    @OneToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "driver_id")
+    @Nullable
+    private Driver driver;
+
+    @OneToMany(fetch = FetchType.LAZY,
+            mappedBy = "car",
+            cascade = {CascadeType.REMOVE, CascadeType.PERSIST, CascadeType.MERGE})
+    @Nonnull
+    private Set<CarComponent> components;
+
+    public Car() {
+        this.components = new HashSet<>();
+    }
+
+    public Car(Driver driver, Set<CarComponent> components) {
+        this.driver = driver;
+        this.components = components;
+    }
+
+    public Driver getDriver() {
+        return driver;
+    }
+
+    public void setDriver(Driver driver) {
+        this.driver = driver;
+    }
+
+    public Set<CarComponent> getComponents() {
+        return components;
+    }
+
+    public void setComponents(Set<CarComponent> components) {
+        this.components = components;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof Car car)) {
+            return false;
+        }
+        return Objects.equals(getDriver(), car.getDriver()) && Objects.equals(getComponents(), car.getComponents());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getDriver(), getComponents());
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/data/model/CarComponent.java b/core/src/main/java/cz/muni/pa165/data/model/CarComponent.java
new file mode 100644
index 0000000000000000000000000000000000000000..f980efdc69661c0e740b19d4074ce0040413e355
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/model/CarComponent.java
@@ -0,0 +1,96 @@
+package cz.muni.pa165.data.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nullable;
+import jakarta.persistence.*;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.Size;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+@Entity
+@Table(name = "car_component")
+public class CarComponent extends DomainObject<Long> implements Serializable {
+
+    @Column(name = "component_type")
+    @Enumerated(EnumType.STRING)
+    @Nonnull
+    private ComponentTypeEnum componentType;
+
+    @Nonnull
+    @Positive
+    private Double weight;
+
+    @NotEmpty
+    @Size(max = 100)
+    private String information;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "car_id")
+    @Nullable
+    @JsonIgnore
+    private Car car;
+
+    public CarComponent() {
+
+    }
+
+    public CarComponent(ComponentTypeEnum type, Double weight, String information, Car car) {
+        this.componentType = type;
+        this.weight = weight;
+        this.information = information;
+        this.car = car;
+    }
+
+    public ComponentTypeEnum getComponentType() {
+        return componentType;
+    }
+
+    public void setComponentType(ComponentTypeEnum componentType) {
+        this.componentType = componentType;
+    }
+
+    public Double getWeight() {
+        return weight;
+    }
+
+    public void setWeight(Double weight) {
+        this.weight = weight;
+    }
+
+    public String getInformation() {
+        return information;
+    }
+
+    public void setInformation(String description) {
+        this.information = description;
+    }
+
+    public Car getCar() {
+        return car;
+    }
+
+    public void setCar(Car car) {
+        this.car = car;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof CarComponent component)) {
+            return false;
+        }
+        return Objects.equals(getId(), component.getId()) && Objects.equals(getComponentType(), component.getComponentType()) && Objects.equals(getWeight(), component.getWeight()) && Objects.equals(getInformation(), component.getInformation());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId(), getComponentType(), getWeight(), getInformation());
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/data/model/Department.java b/core/src/main/java/cz/muni/pa165/data/model/Department.java
new file mode 100644
index 0000000000000000000000000000000000000000..43a7be25ad8c4c6543667c61cb9dcca9c080c3ef
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/model/Department.java
@@ -0,0 +1,70 @@
+package cz.muni.pa165.data.model;
+
+import jakarta.annotation.Nullable;
+import jakarta.persistence.*;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.Size;
+
+import java.io.Serializable;
+import java.util.Objects;
+import java.util.Set;
+
+@Entity
+@Table(name = "department")
+public class Department extends DomainObject<Long> implements Serializable {
+
+    @NotEmpty
+    @Size(max = 100)
+    private String specialization;
+
+    @OneToMany(fetch = FetchType.LAZY,
+            mappedBy = "department",
+            cascade = {CascadeType.REMOVE, CascadeType.PERSIST, CascadeType.MERGE})
+    @Nullable
+    private Set<Engineer> engineers;
+
+    public Department() {
+
+    }
+
+    public Department(String specialization, Set<Engineer> engineers) {
+        this.specialization = specialization;
+        this.engineers = engineers;
+    }
+
+    public String getSpecialization() {
+        return specialization;
+    }
+
+    public void setSpecialization(String specialization) {
+        this.specialization = specialization;
+    }
+
+    public Set<Engineer> getEngineers() {
+        return engineers;
+    }
+
+    public void setEngineers(Set<Engineer> engineers) {
+        this.engineers = engineers;
+    }
+
+    public void addEngineer(Engineer engineer) {
+        this.engineers.add(engineer);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof Department department)) {
+            return false;
+        }
+        return Objects.equals(getSpecialization(), department.getSpecialization());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getSpecialization());
+    }
+}
\ No newline at end of file
diff --git a/core/src/main/java/cz/muni/pa165/data/model/DomainObject.java b/core/src/main/java/cz/muni/pa165/data/model/DomainObject.java
new file mode 100644
index 0000000000000000000000000000000000000000..7071f57d04baa00e3b7fda2e2f2af36deac209c6
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/model/DomainObject.java
@@ -0,0 +1,27 @@
+package cz.muni.pa165.data.model;
+
+import jakarta.annotation.Nonnull;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.MappedSuperclass;
+
+import java.io.Serializable;
+
+/**
+ * @author Michal Badin
+ */
+@MappedSuperclass
+public abstract class DomainObject<PK extends Serializable> implements Serializable {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private PK id;
+
+    public PK getId() {
+        return id;
+    }
+
+    public void setId(PK id) {
+        this.id = id;
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/data/model/Driver.java b/core/src/main/java/cz/muni/pa165/data/model/Driver.java
new file mode 100644
index 0000000000000000000000000000000000000000..58977ff6e11aa090d17b2921093393ee553bd919
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/model/Driver.java
@@ -0,0 +1,139 @@
+package cz.muni.pa165.data.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import cz.muni.pa165.data.enums.CharacteristicsEnum;
+import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nullable;
+import jakarta.persistence.*;
+import jakarta.validation.constraints.*;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.util.Objects;
+import java.util.Set;
+
+@Entity
+@Table(name = "driver")
+public class Driver extends DomainObject<Long> implements Serializable {
+
+    @NotEmpty
+    @Size(max = 35)
+    private String name;
+
+    @NotEmpty
+    @Size(max = 35)
+    private String surname;
+
+    @Nullable
+    @Min(100)
+    @Max(300)
+    private Integer height;
+
+    @Nullable
+    @Past
+    private LocalDate birthday;
+
+    @NotEmpty
+    @Size(max = 20)
+    private String nationality;
+
+    @ElementCollection(targetClass = CharacteristicsEnum.class)
+    @CollectionTable(name = "driver_characteristics", joinColumns = @JoinColumn(name = "driver_id", nullable = false))
+    @Column(name = "characteristic")
+    @Enumerated(EnumType.STRING)
+    @Nonnull
+    private Set<CharacteristicsEnum> characteristics;
+
+    @OneToOne(fetch = FetchType.LAZY,
+            mappedBy = "driver",
+            cascade = {CascadeType.REMOVE, CascadeType.PERSIST, CascadeType.MERGE})
+    @Nullable
+    @JsonIgnore
+    private Car car;
+
+    public Driver() {
+    }
+
+    public Driver(String name, String surname, Integer height, LocalDate birthday, String nationality,
+                  Car car, Set<CharacteristicsEnum> characteristics) {
+        this.name = name;
+        this.surname = surname;
+        this.height = height;
+        this.birthday = birthday;
+        this.nationality = nationality;
+        this.car = car;
+        this.characteristics = characteristics;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getSurname() {
+        return surname;
+    }
+
+    public void setSurname(String surname) {
+        this.surname = surname;
+    }
+
+    public Integer getHeight() {
+        return height;
+    }
+
+    public void setHeight(Integer height) {
+        this.height = height;
+    }
+
+    public LocalDate getBirthday() {
+        return birthday;
+    }
+
+    public void setBirthday(LocalDate birthday) {
+        this.birthday = birthday;
+    }
+
+    public String getNationality() {
+        return nationality;
+    }
+
+    public void setNationality(String nationality) {
+        this.nationality = nationality;
+    }
+
+    public Car getCar() {
+        return car;
+    }
+
+    public void setCar(Car car) {
+        this.car = car;
+    }
+
+    public Set<CharacteristicsEnum> getCharacteristics() {
+        return characteristics;
+    }
+
+    public void setCharacteristics(Set<CharacteristicsEnum> characteristics) {
+        this.characteristics = characteristics;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof Driver driver)) {
+            return false;
+        }
+        return Objects.equals(getName(), driver.getName()) && Objects.equals(getSurname(), driver.getSurname()) && Objects.equals(getHeight(), driver.getHeight()) && Objects.equals(getBirthday(), driver.getBirthday()) && Objects.equals(getNationality(), driver.getNationality());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getName(), getSurname(), getHeight(), getBirthday(), getNationality());
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/data/model/Engineer.java b/core/src/main/java/cz/muni/pa165/data/model/Engineer.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e8e5dbad8c6b3f68ace657aaa60ce16d50dc3cc
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/model/Engineer.java
@@ -0,0 +1,77 @@
+package cz.muni.pa165.data.model;
+
+import jakarta.annotation.Nullable;
+import jakarta.persistence.*;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.Size;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+@Entity
+@Table(name = "engineer")
+public class Engineer extends DomainObject<Long> implements Serializable {
+
+    @NotEmpty
+    @Size(max = 35)
+    private String name;
+
+    @NotEmpty
+    @Size(max = 35)
+    private String surname;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "department_id")
+    @Nullable
+    private Department department;
+
+    public Engineer() {
+
+    }
+
+    public Engineer(String name, String surname, Department department) {
+        this.name = name;
+        this.surname = surname;
+        this.department = department;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getSurname() {
+        return surname;
+    }
+
+    public void setSurname(String surname) {
+        this.surname = surname;
+    }
+
+    public Department getDepartment() {
+        return department;
+    }
+
+    public void setDepartment(Department department) {
+        this.department = department;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof Engineer engineer)) {
+            return false;
+        }
+        return Objects.equals(getName(), engineer.getName()) && Objects.equals(getSurname(), engineer.getSurname());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getName(), getSurname());
+    }
+}
\ No newline at end of file
diff --git a/core/src/main/java/cz/muni/pa165/data/repository/CarComponentRepository.java b/core/src/main/java/cz/muni/pa165/data/repository/CarComponentRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..84f55f975a9d86bfda5f2f3b4a108348455f5962
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/repository/CarComponentRepository.java
@@ -0,0 +1,20 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import cz.muni.pa165.data.model.CarComponent;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface CarComponentRepository extends JpaRepository<CarComponent, Long> {
+    @Query("SELECT c FROM CarComponent c WHERE c.id = :id")
+    Optional<CarComponent> findById(@Param("id") Long id);
+
+    @Query("SELECT c FROM CarComponent c WHERE c.componentType = ?1")
+    List<CarComponent> findByType(ComponentTypeEnum type);
+}
diff --git a/core/src/main/java/cz/muni/pa165/data/repository/CarRepository.java b/core/src/main/java/cz/muni/pa165/data/repository/CarRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..7d4091c81cab8aa9924b98d0b82242d17d216c63
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/repository/CarRepository.java
@@ -0,0 +1,15 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.model.Car;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+public interface CarRepository extends JpaRepository<Car, Long> {
+    @Query("SELECT c FROM Car c LEFT JOIN FETCH c.components cc LEFT JOIN FETCH c.driver d WHERE c.id = :id")
+    Optional<Car> findById(@Param("id") Long id);
+}
diff --git a/core/src/main/java/cz/muni/pa165/data/repository/DepartmentRepository.java b/core/src/main/java/cz/muni/pa165/data/repository/DepartmentRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..db191d1b193575cf60d872035ab261b6a0f8b96f
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/repository/DepartmentRepository.java
@@ -0,0 +1,20 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.model.Department;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface DepartmentRepository extends JpaRepository<Department, Long> {
+
+    @Query("SELECT d FROM Department d LEFT JOIN FETCH d.engineers e WHERE d.id = :id")
+    Optional<Department> findById(@Param("id") Long id);
+
+    @Query("SELECT d FROM Department d LEFT JOIN FETCH d.engineers e")
+    List<Department> findAll();
+}
diff --git a/core/src/main/java/cz/muni/pa165/data/repository/DriverRepository.java b/core/src/main/java/cz/muni/pa165/data/repository/DriverRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..794c2854a52e44f4893f27ec5da37a93a05c0300
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/repository/DriverRepository.java
@@ -0,0 +1,20 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.model.Driver;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Michal Badin
+ */
+public interface DriverRepository extends JpaRepository<Driver, Long> {
+    @Query("SELECT d FROM Driver d WHERE d.id = :id")
+    Optional<Driver> findById(@Param("id") Long id);
+
+    @Query("SELECT d FROM Driver d LEFT JOIN FETCH d.car WHERE d.car IS NULL")
+    List<Driver> findAllTestDrivers();
+}
diff --git a/core/src/main/java/cz/muni/pa165/data/repository/EngineerRepository.java b/core/src/main/java/cz/muni/pa165/data/repository/EngineerRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..bdc3ce4caa855f0582647a9a60ebbfd7f2303123
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/data/repository/EngineerRepository.java
@@ -0,0 +1,10 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.model.Engineer;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface EngineerRepository extends JpaRepository<Engineer, Long> {
+
+}
diff --git a/core/src/main/java/cz/muni/pa165/exceptions/BadRequestException.java b/core/src/main/java/cz/muni/pa165/exceptions/BadRequestException.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd1e3d6286c2d66d7cd9e1252eae7ccdaa857a95
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/exceptions/BadRequestException.java
@@ -0,0 +1,22 @@
+package cz.muni.pa165.exceptions;
+
+public class BadRequestException extends RuntimeException {
+    public BadRequestException() {
+    }
+
+    public BadRequestException(String message) {
+        super(message);
+    }
+
+    public BadRequestException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public BadRequestException(Throwable cause) {
+        super(cause);
+    }
+
+    public BadRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/exceptions/ResourceNotFoundException.java b/core/src/main/java/cz/muni/pa165/exceptions/ResourceNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..b602ad7bc2687e7b2b949d2f65535efa065bc124
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/exceptions/ResourceNotFoundException.java
@@ -0,0 +1,8 @@
+package cz.muni.pa165.exceptions;
+
+public class ResourceNotFoundException extends RuntimeException {
+    public ResourceNotFoundException(Class<?> clazz, Long id) {
+        super(String.format("%s with id %s was not found", clazz, id));
+    }
+}
+
diff --git a/core/src/main/java/cz/muni/pa165/facade/CarComponentFacade.java b/core/src/main/java/cz/muni/pa165/facade/CarComponentFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1c0687b802e00f6db1f825211a33a97814984a4
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/facade/CarComponentFacade.java
@@ -0,0 +1,56 @@
+package cz.muni.pa165.facade;
+
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import cz.muni.pa165.generated.core.model.CarComponentCreateDto;
+import cz.muni.pa165.generated.core.model.CarComponentDto;
+import cz.muni.pa165.generated.core.model.CarComponentUpdateDto;
+import cz.muni.pa165.mappers.CarComponentMapper;
+import cz.muni.pa165.service.CarComponentService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+@Transactional
+public class CarComponentFacade {
+
+    private final CarComponentService componentService;
+    private final CarComponentMapper componentMapper;
+
+    @Autowired
+    public CarComponentFacade(CarComponentService componentService, CarComponentMapper componentMapper) {
+        this.componentService = componentService;
+        this.componentMapper = componentMapper;
+    }
+
+    @Cacheable(cacheNames = "components", key = "#id")
+    @Transactional(readOnly = true)
+    public CarComponentDto findById(Long id) {
+        return componentMapper.mapToDto(componentService.findById(id));
+    }
+
+    @Transactional(readOnly = true)
+    public List<CarComponentDto> findAll() {
+        return componentMapper.mapToList(componentService.findAll());
+    }
+
+    @Transactional(readOnly = true)
+    public List<CarComponentDto> findAllByType(ComponentTypeEnum type) {
+        return componentMapper.mapToList(componentService.findAllByType(type));
+    }
+
+    public CarComponentDto create(CarComponentCreateDto componentCreateDto) {
+        return componentMapper.mapToDto(componentService.create(componentMapper.mapFromCreateDto(componentCreateDto)));
+    }
+
+    public void delete(Long id) {
+        componentService.delete(id);
+    }
+
+    public CarComponentDto update(Long id, CarComponentUpdateDto componentUpdateDto) {
+        return componentMapper.mapToDto(componentService.update(id, componentMapper.mapFromUpdateDto(componentUpdateDto)));
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/facade/CarFacade.java b/core/src/main/java/cz/muni/pa165/facade/CarFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..5053230df236387ebc32436dfdc3b5307824d35b
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/facade/CarFacade.java
@@ -0,0 +1,56 @@
+package cz.muni.pa165.facade;
+
+import cz.muni.pa165.generated.core.model.CarDto;
+import cz.muni.pa165.generated.core.model.CarDtoPage;
+import cz.muni.pa165.mappers.CarMapper;
+import cz.muni.pa165.service.CarService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+@Transactional
+public class CarFacade {
+
+    private final CarService carService;
+    private final CarMapper carMapper;
+
+    @Autowired
+    public CarFacade(CarService carService, CarMapper carMapper) {
+        this.carService = carService;
+        this.carMapper = carMapper;
+    }
+
+    @Cacheable(cacheNames = "cars", key = "#id")
+    @Transactional(readOnly = true)
+    public CarDto get(Long id) {
+        return carMapper.mapToDto(carService.get(id));
+    }
+
+    public CarDto create(List<Long> componentIds) {
+        return carMapper.mapToDto(carService.create(componentIds));
+    }
+
+    public void delete(Long id) {
+        carService.delete(id);
+    }
+
+    public CarDto update(Long id, List<Long> componentIds) {
+        return carMapper.mapToDto(carService.update(id, componentIds));
+    }
+
+    public CarDto setDriver(Long carId, Long driverId) {
+        return carMapper.mapToDto(carService.setDriver(carId, driverId));
+    }
+
+    public void removeDriver(Long carId) {
+        carService.removeDriver(carId);
+    }
+
+    public CarDtoPage getAllCars(Integer page, Integer size) {
+        return carMapper.mapToDtoPage(carService.findAll(page, size));
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/facade/DepartmentFacade.java b/core/src/main/java/cz/muni/pa165/facade/DepartmentFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..327b1c72b36ee9e546aad65c75865d1fdb38615e
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/facade/DepartmentFacade.java
@@ -0,0 +1,54 @@
+package cz.muni.pa165.facade;
+
+import cz.muni.pa165.generated.core.model.DepartmentCreateDto;
+import cz.muni.pa165.generated.core.model.DepartmentDto;
+import cz.muni.pa165.generated.core.model.DepartmentUpdateDto;
+import cz.muni.pa165.mappers.DepartmentMapper;
+import cz.muni.pa165.service.DepartmentService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+@Transactional
+public class DepartmentFacade {
+
+    private final DepartmentService departmentService;
+    private final DepartmentMapper mapper;
+
+    @Autowired
+    public DepartmentFacade(DepartmentService departmentService, DepartmentMapper departmentMapper) {
+        this.departmentService = departmentService;
+        this.mapper = departmentMapper;
+    }
+
+    public DepartmentDto createDepartment(DepartmentCreateDto departmentCreateDto) {
+        return mapper.mapToDto(departmentService.create(mapper.mapFromCreateDto(departmentCreateDto)));
+    }
+
+    public DepartmentDto addEngineer(Long departmentId, Long engineerId) {
+        return mapper.mapToDto(departmentService.addEngineer(
+                departmentId, engineerId)
+        );
+    }
+
+    public DepartmentDto updateSpecialization(Long id, DepartmentUpdateDto departmentUpdateDto) {
+        return mapper.mapToDto(departmentService.updateSpecialization(id, mapper.mapFromUpdateDto(departmentUpdateDto)));
+    }
+
+    public void deleteDepartment(Long id) {
+        departmentService.delete(id);
+    }
+
+    @Transactional(readOnly = true)
+    public DepartmentDto findById(Long id) {
+        return mapper.mapToDto(departmentService.findById(id));
+    }
+
+    @Transactional(readOnly = true)
+    public List<DepartmentDto> findAll() {
+        return mapper.mapToList(departmentService.findAll());
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/facade/DriverFacade.java b/core/src/main/java/cz/muni/pa165/facade/DriverFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..09aeb266284168475549c887326cc25dbaf11c93
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/facade/DriverFacade.java
@@ -0,0 +1,57 @@
+package cz.muni.pa165.facade;
+
+import cz.muni.pa165.generated.core.model.DriverCreateDto;
+import cz.muni.pa165.generated.core.model.DriverDto;
+import cz.muni.pa165.generated.core.model.DriverUpdateDto;
+import cz.muni.pa165.mappers.DriverMapper;
+import cz.muni.pa165.service.DriverService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @author Michal Badin
+ */
+@Service
+@Transactional
+public class DriverFacade {
+    private final DriverService driverService;
+    private final DriverMapper driverMapper;
+
+    @Autowired
+    DriverFacade(DriverService driverService, DriverMapper driverMapper) {
+        this.driverService = driverService;
+        this.driverMapper = driverMapper;
+    }
+
+    @Cacheable(cacheNames = "drivers", key = "#id")
+    @Transactional(readOnly = true)
+    public DriverDto findById(Long id) {
+        return driverMapper.mapToDto(driverService.findById(id));
+    }
+
+    public DriverDto create(DriverCreateDto driverCreateDto) {
+        return driverMapper.mapToDto(driverService.create(driverMapper.mapFromCreateDto(driverCreateDto)));
+    }
+
+    public void delete(Long id) {
+        driverService.delete(id);
+    }
+
+    public DriverDto update(Long id, DriverUpdateDto driverUpdateDto) {
+        return driverMapper.mapToDto(driverService.update(id, driverMapper.mapFromUpdateDto(driverUpdateDto)));
+    }
+
+    @Transactional(readOnly = true)
+    public List<DriverDto> getAllDrivers() {
+        return driverMapper.mapToList(driverService.findAll());
+    }
+
+    @Transactional(readOnly = true)
+    public List<DriverDto> getAllTestDrivers() {
+        return driverMapper.mapToList(driverService.findAllTestDrivers());
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/facade/EngineerFacade.java b/core/src/main/java/cz/muni/pa165/facade/EngineerFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..5565dd14f9d7409b6176f513511fe66ab8cabd1b
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/facade/EngineerFacade.java
@@ -0,0 +1,40 @@
+package cz.muni.pa165.facade;
+
+import cz.muni.pa165.generated.core.model.EngineerCreateDto;
+import cz.muni.pa165.generated.core.model.EngineerDto;
+import cz.muni.pa165.mappers.EngineerMapper;
+import cz.muni.pa165.service.EngineerService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+@Transactional
+public class EngineerFacade {
+    private final EngineerService engineerService;
+    private final EngineerMapper engineerMapper;
+
+    @Autowired
+    public EngineerFacade(EngineerService engineerService, EngineerMapper engineerMapper) {
+        this.engineerService = engineerService;
+        this.engineerMapper = engineerMapper;
+    }
+
+    public EngineerDto createEngineer(EngineerCreateDto engineerCreateDto) {
+        return engineerMapper.mapToDto(engineerService.create(engineerMapper.mapFromCreateDto(engineerCreateDto)));
+    }
+
+    public void deleteEngineer(Long id) {
+        engineerService.delete(id);
+    }
+
+    public EngineerDto getEngineer(Long id) {
+        return engineerMapper.mapToDto(engineerService.findById(id));
+    }
+
+    public List<EngineerDto> getEngineers() {
+        return engineerMapper.mapToList(engineerService.findAll());
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/mappers/CarComponentMapper.java b/core/src/main/java/cz/muni/pa165/mappers/CarComponentMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..543d75ce10d88f7e765e8ad9c336d1fd601b39f2
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/mappers/CarComponentMapper.java
@@ -0,0 +1,23 @@
+package cz.muni.pa165.mappers;
+
+import cz.muni.pa165.data.model.CarComponent;
+import cz.muni.pa165.generated.core.model.CarComponentCreateDto;
+import cz.muni.pa165.generated.core.model.CarComponentDto;
+import cz.muni.pa165.generated.core.model.CarComponentUpdateDto;
+import org.mapstruct.Mapper;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring")
+public interface CarComponentMapper {
+
+    CarComponentDto mapToDto(CarComponent component);
+
+    CarComponent mapFromDto(CarComponentDto componentDto);
+
+    CarComponent mapFromCreateDto(CarComponentCreateDto componentCreateDto);
+
+    CarComponent mapFromUpdateDto(CarComponentUpdateDto componentUpdateDto);
+
+    List<CarComponentDto> mapToList(List<CarComponent> cars);
+}
diff --git a/core/src/main/java/cz/muni/pa165/mappers/CarMapper.java b/core/src/main/java/cz/muni/pa165/mappers/CarMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..11af31ba7e2414692d5e0d85629841a583476e30
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/mappers/CarMapper.java
@@ -0,0 +1,29 @@
+package cz.muni.pa165.mappers;
+
+import cz.muni.pa165.data.model.Car;
+import cz.muni.pa165.generated.core.model.CarDto;
+import cz.muni.pa165.generated.core.model.CarDtoPage;
+import org.mapstruct.Mapper;
+import org.springframework.data.domain.Page;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring")
+public interface CarMapper {
+
+    CarDto mapToDto(Car car);
+
+    List<CarDto> mapToList(List<Car> cars);
+
+    default CarDtoPage mapToDtoPage(Page<Car> cars) {
+        return new CarDtoPage()
+                .content(mapToList(cars.getContent()))
+                .pageable(cars.getPageable())
+                .size(cars.getSize())
+                .totalPages(cars.getTotalPages())
+                .totalElements((int) cars.getTotalElements())
+                .sort(cars.getSort())
+                .number(cars.getNumber())
+                .isLast(cars.isLast());
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/mappers/DepartmentMapper.java b/core/src/main/java/cz/muni/pa165/mappers/DepartmentMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..d60fac9bb7bfcd35fcb18cc8060a9fe771ea8e25
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/mappers/DepartmentMapper.java
@@ -0,0 +1,24 @@
+package cz.muni.pa165.mappers;
+
+import cz.muni.pa165.data.model.Department;
+import cz.muni.pa165.data.model.Engineer;
+import cz.muni.pa165.generated.core.model.DepartmentCreateDto;
+import cz.muni.pa165.generated.core.model.DepartmentDto;
+import cz.muni.pa165.generated.core.model.DepartmentUpdateDto;
+import cz.muni.pa165.generated.core.model.EngineerDto;
+import org.mapstruct.Mapper;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring")
+public interface DepartmentMapper {
+    DepartmentDto mapToDto(Department department);
+
+    Engineer mapFromEngineerDto(EngineerDto engineerDto);
+
+    Department mapFromCreateDto(DepartmentCreateDto departmentCreateDto);
+
+    Department mapFromUpdateDto(DepartmentUpdateDto departmentUpdateDto);
+
+    List<DepartmentDto> mapToList(List<Department> departmentList);
+}
diff --git a/core/src/main/java/cz/muni/pa165/mappers/DriverMapper.java b/core/src/main/java/cz/muni/pa165/mappers/DriverMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..61b0f52ab7ad4101857ae0b0565e84a84ed070d6
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/mappers/DriverMapper.java
@@ -0,0 +1,23 @@
+package cz.muni.pa165.mappers;
+
+import cz.muni.pa165.data.model.Driver;
+import cz.muni.pa165.generated.core.model.DriverCreateDto;
+import cz.muni.pa165.generated.core.model.DriverDto;
+import cz.muni.pa165.generated.core.model.DriverUpdateDto;
+import org.mapstruct.Mapper;
+
+import java.util.List;
+
+/**
+ * @author Michal Badin
+ */
+@Mapper(componentModel = "spring")
+public interface DriverMapper {
+    DriverDto mapToDto(Driver driver);
+
+    Driver mapFromCreateDto(DriverCreateDto carCreateDto);
+
+    Driver mapFromUpdateDto(DriverUpdateDto carUpdateDto);
+
+    List<DriverDto> mapToList(List<Driver> drivers);
+}
diff --git a/core/src/main/java/cz/muni/pa165/mappers/EngineerMapper.java b/core/src/main/java/cz/muni/pa165/mappers/EngineerMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..8889e414c772db6aa5b8e23662bf7d345ae66d90
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/mappers/EngineerMapper.java
@@ -0,0 +1,17 @@
+package cz.muni.pa165.mappers;
+
+import cz.muni.pa165.data.model.Engineer;
+import cz.muni.pa165.generated.core.model.EngineerCreateDto;
+import cz.muni.pa165.generated.core.model.EngineerDto;
+import org.mapstruct.Mapper;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring")
+public interface EngineerMapper {
+    EngineerDto mapToDto(Engineer engineer);
+
+    Engineer mapFromCreateDto(EngineerCreateDto engineerCreateDto);
+
+    List<EngineerDto> mapToList(List<Engineer> engineers);
+}
diff --git a/core/src/main/java/cz/muni/pa165/rest/CarComponentController.java b/core/src/main/java/cz/muni/pa165/rest/CarComponentController.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5a7c58e6f12bfc9e9ead566a84bcfa6f4e45262
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/rest/CarComponentController.java
@@ -0,0 +1,76 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import cz.muni.pa165.facade.CarComponentFacade;
+import cz.muni.pa165.generated.core.api.CarComponentServiceApiDelegate;
+import cz.muni.pa165.generated.core.model.CarComponentCreateDto;
+import cz.muni.pa165.generated.core.model.CarComponentDto;
+import cz.muni.pa165.generated.core.model.CarComponentType;
+import cz.muni.pa165.generated.core.model.CarComponentUpdateDto;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+public class CarComponentController implements CarComponentServiceApiDelegate {
+
+    private static final Logger log = LoggerFactory.getLogger(CarComponentController.class);
+
+
+    private final CarComponentFacade componentFacade;
+
+    @Autowired
+    public CarComponentController(CarComponentFacade componentFacade) {
+        this.componentFacade = componentFacade;
+    }
+
+
+    @Override
+    public ResponseEntity<CarComponentDto> getCarComponent(Long id) {
+        log.debug("getCarComponent() called");
+
+        return ResponseEntity.ok(componentFacade.findById(id));
+    }
+
+    public ResponseEntity<List<CarComponentDto>> getAllCarComponents() {
+        log.debug("getAllCarComponents() called");
+
+        return ResponseEntity.ok(componentFacade.findAll());
+    }
+
+    public ResponseEntity<List<CarComponentDto>> getCarComponentsByType(CarComponentType componentType) {
+        log.debug("getCarComponentsByType() called");
+
+        List<CarComponentDto> components = componentFacade.findAllByType(ComponentTypeEnum.fromValue(componentType.getValue()));
+        return new ResponseEntity<>(components, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<CarComponentDto> createCarComponent(CarComponentCreateDto componentCreateDto) {
+        log.debug("createCarComponent() called");
+
+        return ResponseEntity.ok(componentFacade.create(componentCreateDto));
+    }
+
+    @Override
+    public ResponseEntity<Void> deleteCarComponent(Long id) {
+        log.debug("deleteCarComponent() called");
+
+        componentFacade.delete(id);
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<CarComponentDto> updateCarComponent(Long id, CarComponentUpdateDto componentUpdateDto) {
+        log.debug("updateCarComponent() called");
+
+        return new ResponseEntity<>(componentFacade.update(id, componentUpdateDto), HttpStatus.OK);
+
+    }
+
+}
diff --git a/core/src/main/java/cz/muni/pa165/rest/CarController.java b/core/src/main/java/cz/muni/pa165/rest/CarController.java
index add414e893f2e43854aa2ff80673d527c4a992aa..aaae78cff3a03f6538d470c924410e0032e0d997 100644
--- a/core/src/main/java/cz/muni/pa165/rest/CarController.java
+++ b/core/src/main/java/cz/muni/pa165/rest/CarController.java
@@ -1,33 +1,69 @@
 package cz.muni.pa165.rest;
 
+import cz.muni.pa165.facade.CarFacade;
 import cz.muni.pa165.generated.core.api.CarServiceApiDelegate;
-import cz.muni.pa165.generated.core.model.Car;
-import cz.muni.pa165.generated.core.model.CreateCarRequest;
+import cz.muni.pa165.generated.core.model.CarDto;
+import cz.muni.pa165.generated.core.model.CarDtoPage;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
 
-public class CarController implements CarServiceApiDelegate {
+import java.util.List;
 
+@Component
+public class CarController implements CarServiceApiDelegate {
     private static final Logger log = LoggerFactory.getLogger(CarController.class);
+    private final CarFacade carFacade;
+
+    @Autowired
+    public CarController(CarFacade carFacade) {
+        this.carFacade = carFacade;
+    }
 
     @Override
-    public ResponseEntity<Car> createCar(CreateCarRequest createCarRequest) {
+    public ResponseEntity<CarDto> createCar(List<Long> componentIds) {
         log.debug("createCar() called");
-        return new ResponseEntity<>(
-                new Car()
-                        .id(1)
-                        .color(createCarRequest.getColor())
-                        .components(createCarRequest.getComponents()), HttpStatus.OK);
+        return ResponseEntity.ok(carFacade.create(componentIds));
+    }
+
+    @Override
+    public ResponseEntity<CarDto> setDriver(Long carId, Long driverId) {
+        log.debug("setDriver() called");
+        return new ResponseEntity<>(carFacade.setDriver(carId, driverId), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<Void> removeDriver(Long carId) {
+        log.debug("removeDriver() called");
+        carFacade.removeDriver(carId);
+        return new ResponseEntity<>(HttpStatus.OK);
     }
 
     @Override
-    public ResponseEntity<Car> getCar(Integer id) {
+    public ResponseEntity<CarDto> getCar(Long id) {
         log.debug("getCar() called");
-        return new ResponseEntity<>(
-                new Car()
-                        .id(1)
-                        .components(null), HttpStatus.OK);
+        return ResponseEntity.ok(carFacade.get(id));
+    }
+
+    @Override
+    public ResponseEntity<Void> deleteCar(Long id) {
+        log.debug("deleteCar() called");
+        carFacade.delete(id);
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<CarDto> updateCar(Long id, List<Long> componentIds) {
+        log.debug("updateCar() called");
+        return new ResponseEntity<>(carFacade.update(id, componentIds), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<CarDtoPage> getAllCars(Integer page, Integer size) {
+        log.debug("getAllCars() called");
+        return new ResponseEntity<>(carFacade.getAllCars(page, size), HttpStatus.OK);
     }
 }
diff --git a/core/src/main/java/cz/muni/pa165/rest/ComponentController.java b/core/src/main/java/cz/muni/pa165/rest/ComponentController.java
deleted file mode 100644
index aea951746d0bbfe8ea3a6609102e2b3ea6ce2ceb..0000000000000000000000000000000000000000
--- a/core/src/main/java/cz/muni/pa165/rest/ComponentController.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package cz.muni.pa165.rest;
-
-import cz.muni.pa165.generated.core.api.ComponentServiceApiDelegate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Component;
-
-import java.math.BigDecimal;
-
-@Component
-public class ComponentController implements ComponentServiceApiDelegate {
-
-    private static final Logger log = LoggerFactory.getLogger(ComponentController.class);
-
-    @Override
-    public ResponseEntity<cz.muni.pa165.generated.core.model.Component> getComponent(Integer id) {
-        log.debug("getComponent() called");
-        return new ResponseEntity<>(
-                new cz.muni.pa165.generated.core.model.Component()
-                        .id(1)
-                        .weight(BigDecimal.valueOf(50.5)), HttpStatus.OK);
-    }
-}
diff --git a/core/src/main/java/cz/muni/pa165/rest/DBManagementController.java b/core/src/main/java/cz/muni/pa165/rest/DBManagementController.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd6d2f7c4c1c2bd6318a435b3ac8ed17b1da4cf4
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/rest/DBManagementController.java
@@ -0,0 +1,42 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.generated.core.api.DbManagementServiceApiDelegate;
+import cz.muni.pa165.generated.core.model.SuccessfullyProcessed;
+import cz.muni.pa165.service.DBManagementService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Michal Badin
+ */
+@Profile("dev")
+@Component
+public class DBManagementController implements DbManagementServiceApiDelegate {
+    private static final Logger log = LoggerFactory.getLogger(DBManagementController.class);
+
+    private final DBManagementService dbManagementService;
+
+    @Autowired
+    public DBManagementController(DBManagementService dbManagementService) {
+        this.dbManagementService = dbManagementService;
+    }
+
+    @Override
+    public ResponseEntity<SuccessfullyProcessed> seedDB() {
+        log.debug("seedDB() called");
+        dbManagementService.seed();
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<SuccessfullyProcessed> clearDB() {
+        log.debug("clearDB() called");
+        dbManagementService.clear();
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/rest/DepartmentController.java b/core/src/main/java/cz/muni/pa165/rest/DepartmentController.java
new file mode 100644
index 0000000000000000000000000000000000000000..eaeff7fc2c155c6565c10d7188db421f69d5c71c
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/rest/DepartmentController.java
@@ -0,0 +1,76 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.facade.DepartmentFacade;
+import cz.muni.pa165.generated.core.api.DepartmentServiceApiDelegate;
+import cz.muni.pa165.generated.core.model.DepartmentCreateDto;
+import cz.muni.pa165.generated.core.model.DepartmentDto;
+import cz.muni.pa165.generated.core.model.DepartmentUpdateDto;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author Alžbeta Hajná
+ */
+
+@Component
+public class DepartmentController implements DepartmentServiceApiDelegate {
+    private static final Logger log = LoggerFactory.getLogger(DepartmentController.class);
+
+    private final DepartmentFacade departmentFacade;
+
+    @Autowired
+    public DepartmentController(DepartmentFacade departmentFacade) {
+        this.departmentFacade = departmentFacade;
+    }
+
+    @Override
+    public ResponseEntity<DepartmentDto> addEngineer(Long departmentId, Long engineerId) {
+        log.debug("addEngineer() called");
+        return new ResponseEntity<>(departmentFacade.addEngineer(departmentId, engineerId), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<DepartmentDto> createDepartment(DepartmentCreateDto departmentCreateDto) {
+        log.debug("createDepartment() called");
+
+        return new ResponseEntity<>(departmentFacade.createDepartment(departmentCreateDto), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<Void> deleteDepartment(Long id) {
+        log.debug("deleteDepartment() called");
+
+        departmentFacade.deleteDepartment(id);
+
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<DepartmentDto> getDepartment(Long id) {
+        log.debug("getDepartment() called");
+
+        return new ResponseEntity<>(departmentFacade.findById(id), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<List<DepartmentDto>> getDepartments() {
+        log.debug("getDepartments() called");
+        return new ResponseEntity<>(departmentFacade.findAll(), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<DepartmentDto> updateSpecialization(Long id, DepartmentUpdateDto departmentUpdateDto) {
+        log.debug("updateSpecialization() called");
+        return new ResponseEntity<>(departmentFacade.updateSpecialization(id, departmentUpdateDto), HttpStatus.OK);
+    }
+
+    private DepartmentDto findById(Long id) {
+        return departmentFacade.findById(id);
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/rest/DriverController.java b/core/src/main/java/cz/muni/pa165/rest/DriverController.java
new file mode 100644
index 0000000000000000000000000000000000000000..26f7fd55a2a396abcc6b2edc4d0fccba490488b1
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/rest/DriverController.java
@@ -0,0 +1,71 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.facade.DriverFacade;
+import cz.muni.pa165.generated.core.api.DriverServiceApiDelegate;
+import cz.muni.pa165.generated.core.model.DriverCreateDto;
+import cz.muni.pa165.generated.core.model.DriverDto;
+import cz.muni.pa165.generated.core.model.DriverUpdateDto;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author Michal Badin
+ */
+@Component
+public class DriverController implements DriverServiceApiDelegate {
+    private static final Logger log = LoggerFactory.getLogger(DriverController.class);
+    private final DriverFacade driverFacade;
+
+    @Autowired
+    public DriverController(DriverFacade driverFacade) {
+        this.driverFacade = driverFacade;
+    }
+
+    @Override
+    public ResponseEntity<Void> deleteDriver(Long id) {
+        log.debug("deleteDrive() called");
+        driverFacade.delete(id);
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<List<DriverDto>> getAllTestDrivers() {
+        log.debug("getAllTestDrivers() called");
+        List<DriverDto> drivers = driverFacade.getAllTestDrivers();
+        return new ResponseEntity<>(drivers, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<DriverDto> updateDriver(Long id, DriverUpdateDto driverUpdateDto) {
+        log.debug("updateDriver() called");
+        DriverDto driver = driverFacade.update(id, driverUpdateDto);
+        return new ResponseEntity<>(driver, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<List<DriverDto>> getAllDrivers() {
+        log.debug("getAllDriver() called");
+        List<DriverDto> drivers = driverFacade.getAllDrivers();
+        return new ResponseEntity<>(drivers, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<DriverDto> getDriver(Long id) {
+        log.debug("getDriver() called");
+        DriverDto driver = driverFacade.findById(id);
+        return new ResponseEntity<>(driver, HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<DriverDto> createDriver(DriverCreateDto driverCreateDto) {
+        log.debug("createDriver() called");
+        DriverDto driver = driverFacade.create(driverCreateDto);
+        return new ResponseEntity<>(driver, HttpStatus.OK);
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/rest/EngineerController.java b/core/src/main/java/cz/muni/pa165/rest/EngineerController.java
new file mode 100644
index 0000000000000000000000000000000000000000..79e68c9390bfd2a2958558ddcece76b3e9473bcf
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/rest/EngineerController.java
@@ -0,0 +1,51 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.facade.EngineerFacade;
+import cz.muni.pa165.generated.core.api.EngineerServiceApiDelegate;
+import cz.muni.pa165.generated.core.model.EngineerCreateDto;
+import cz.muni.pa165.generated.core.model.EngineerDto;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+public class EngineerController implements EngineerServiceApiDelegate {
+    private static final Logger log = LoggerFactory.getLogger(EngineerController.class);
+
+    private final EngineerFacade engineerFacade;
+
+    @Autowired
+    public EngineerController(EngineerFacade engineerFacade) {
+        this.engineerFacade = engineerFacade;
+    }
+
+    @Override
+    public ResponseEntity<EngineerDto> createEngineer(EngineerCreateDto engineerCreateDto) {
+        log.debug("createEngineer() called with createDao:" + engineerCreateDto);
+        return new ResponseEntity<>(engineerFacade.createEngineer(engineerCreateDto), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<Void> deleteEngineer(Long id) {
+        log.debug("deleteEngineer() called with id:" + id);
+        engineerFacade.deleteEngineer(id);
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<EngineerDto> getEngineer(Long id) {
+        log.debug("getEngineer() called with id:" + id);
+        return new ResponseEntity<>(engineerFacade.getEngineer(id), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<List<EngineerDto>> getEngineers() {
+        log.debug("getEngineers() called");
+        return new ResponseEntity<>(engineerFacade.getEngineers(), HttpStatus.OK);
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/rest/exceptionhandling/ApiError.java b/core/src/main/java/cz/muni/pa165/rest/exceptionhandling/ApiError.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e1161dcad4805649ce3757c0c7983dd52f30e9c
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/rest/exceptionhandling/ApiError.java
@@ -0,0 +1,62 @@
+package cz.muni.pa165.rest.exceptionhandling;
+
+import org.springframework.http.HttpStatus;
+
+import java.time.LocalDateTime;
+
+public class ApiError {
+
+    private LocalDateTime timestamp;
+    private HttpStatus status;
+    private String message;
+    private String path;
+
+    public ApiError(LocalDateTime timestamp, HttpStatus status, String message, String path) {
+        this.timestamp = timestamp;
+        this.status = status;
+        this.message = message;
+        this.path = path;
+    }
+
+    public LocalDateTime getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(LocalDateTime timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public HttpStatus getStatus() {
+        return status;
+    }
+
+    public void setStatus(HttpStatus status) {
+        this.status = status;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    @Override
+    public String toString() {
+        return "ApiError{" +
+                "timestamp=" + timestamp +
+                ", status=" + status +
+                ", message='" + message + '\'' +
+                ", path='" + path + '\'' +
+                '}';
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/rest/exceptionhandling/CustomRestGlobalExceptionHandling.java b/core/src/main/java/cz/muni/pa165/rest/exceptionhandling/CustomRestGlobalExceptionHandling.java
new file mode 100644
index 0000000000000000000000000000000000000000..a84c958a78b5f8144d380134bc827b9ca7ee7ee4
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/rest/exceptionhandling/CustomRestGlobalExceptionHandling.java
@@ -0,0 +1,66 @@
+package cz.muni.pa165.rest.exceptionhandling;
+
+import cz.muni.pa165.exceptions.BadRequestException;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.util.UrlPathHelper;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+
+@RestControllerAdvice
+public class CustomRestGlobalExceptionHandling {
+
+    private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper();
+
+    @ExceptionHandler({ResourceNotFoundException.class})
+    public ResponseEntity<ApiError> handleResourceNotFound(
+            final ResourceNotFoundException ex, final HttpServletRequest request) {
+        final ApiError apiError = new ApiError(
+                LocalDateTime.now(Clock.systemUTC()),
+                HttpStatus.NOT_FOUND,
+                ex.getLocalizedMessage(),
+                URL_PATH_HELPER.getRequestUri(request));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    @ExceptionHandler({BadRequestException.class})
+    public ResponseEntity<ApiError> handleBadRequest(
+            final BadRequestException ex, final HttpServletRequest request) {
+        final ApiError apiError = new ApiError(
+                LocalDateTime.now(Clock.systemUTC()),
+                HttpStatus.BAD_REQUEST,
+                ex.getLocalizedMessage(),
+                URL_PATH_HELPER.getRequestUri(request));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle all the exceptions not matched by above-mentioned definitions.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @return the response entity
+     */
+    @ExceptionHandler({Exception.class})
+    public ResponseEntity<ApiError> handleAll(final Exception ex, HttpServletRequest request) {
+        final ApiError apiError = new ApiError(
+                LocalDateTime.now(Clock.systemUTC()),
+                HttpStatus.INTERNAL_SERVER_ERROR,
+                getInitialException(ex).getLocalizedMessage(),
+                URL_PATH_HELPER.getRequestUri(request));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    private Exception getInitialException(Exception ex) {
+        while (ex.getCause() != null) {
+            ex = (Exception) ex.getCause();
+        }
+        return ex;
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/service/CarComponentService.java b/core/src/main/java/cz/muni/pa165/service/CarComponentService.java
new file mode 100644
index 0000000000000000000000000000000000000000..6fd277748b653db0b3b583ccc8af40312f4d2a8f
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/service/CarComponentService.java
@@ -0,0 +1,95 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import cz.muni.pa165.data.model.CarComponent;
+import cz.muni.pa165.data.repository.CarComponentRepository;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.Resource;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClientException;
+
+import java.util.*;
+
+@Service
+public class CarComponentService extends DomainService<CarComponent> {
+
+    private static final Logger log = LoggerFactory.getLogger(CarComponentService.class);
+    private static final String NOTIFICATION_MODULE_DOCKER = "http://notification:8083";
+    private static final String NOTIFICATION_MODULE_LOCALHOST = "http://localhost:8083";
+    private static final String NOTIFICATION_MODULE_URI = "/notification/component";
+    private final CarComponentRepository componentRepository;
+    private final WebClient webClient;
+    @Value("${notification.receivers}")
+    private final List<String> notificationReceivers = new ArrayList<>();
+    private String NOTIFICATION_MODULE_URL;
+
+    @Autowired
+    public CarComponentService(CarComponentRepository repository, WebClient.Builder webClientBuilder) {
+        super(repository, CarComponent.class);
+        this.componentRepository = repository;
+
+        //if running in docker, modules cannot communicate through localhost
+        if (System.getenv("DOCKER") != null) {
+            NOTIFICATION_MODULE_URL = NOTIFICATION_MODULE_DOCKER;
+        } else {
+            NOTIFICATION_MODULE_URL = NOTIFICATION_MODULE_LOCALHOST;
+        }
+        this.webClient = webClientBuilder.baseUrl(NOTIFICATION_MODULE_URL).build();
+    }
+
+    @Override
+    public CarComponent create(CarComponent component) {
+        CarComponent savedComponent = repository.save(component);
+        try {
+            sendPostRequest(savedComponent);
+        } catch (WebClientException e) {
+            log.debug(String.format("The notification module is not reachable on the URL: %s, exception %s", NOTIFICATION_MODULE_URL + NOTIFICATION_MODULE_URI, e));
+        }
+        return savedComponent;
+    }
+
+    public CarComponent update(Long id, CarComponent component) {
+        Optional<CarComponent> dbComponent = componentRepository.findById(id);
+        if (dbComponent.isEmpty()) {
+            throw new ResourceNotFoundException(entityClass, id);
+        }
+        dbComponent.get().setComponentType(component.getComponentType());
+        dbComponent.get().setInformation(component.getInformation());
+        dbComponent.get().setWeight(component.getWeight());
+
+        return componentRepository.save(dbComponent.get());
+    }
+
+    @Transactional(readOnly = true)
+    public CarComponent findById(Long id) {
+        return componentRepository.findById(id)
+                .orElseThrow(() ->
+                        new ResourceNotFoundException(entityClass, id));
+    }
+
+    @Transactional(readOnly = true)
+    public List<CarComponent> findAllByType(ComponentTypeEnum type) {
+        return componentRepository.findByType(type);
+    }
+
+    private void sendPostRequest(CarComponent component) {
+        Map<String, Object> notification = new HashMap<>();
+        notification.put("carComponent", component);
+        notification.put("receivers", notificationReceivers);
+
+        webClient.post()
+                .uri(NOTIFICATION_MODULE_URI)
+                .contentType(MediaType.APPLICATION_JSON)
+                .bodyValue(notification)
+                .retrieve()
+                .bodyToMono(Resource.class)
+                .block();
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/service/CarService.java b/core/src/main/java/cz/muni/pa165/service/CarService.java
new file mode 100644
index 0000000000000000000000000000000000000000..0f8d405ae36f5012233b5ea310c87d2d6281f0f8
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/service/CarService.java
@@ -0,0 +1,214 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.model.Car;
+import cz.muni.pa165.data.model.CarComponent;
+import cz.muni.pa165.data.model.Driver;
+import cz.muni.pa165.data.repository.CarComponentRepository;
+import cz.muni.pa165.data.repository.CarRepository;
+import cz.muni.pa165.data.repository.DriverRepository;
+import cz.muni.pa165.exceptions.BadRequestException;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.core.io.Resource;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClientException;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+@Service
+public class CarService extends DomainService<Car> {
+    private static final Logger log = LoggerFactory.getLogger(CarService.class);
+    private static final String VISUALIZATION_MODULE_DOCKER = "http://visualization:8082";
+    private static final String VISUALIZATION_MODULE_LOCALHOST = "http://localhost:8082";
+    private String VISUALIZATION_MODULE_URL;
+    private static final String VISUALIZATION_MODULE_URI = "/visualization";
+    private final DriverRepository driverRepository;
+    private final CarRepository carRepository;
+    private final CarComponentRepository carComponentRepository;
+    private final WebClient webClient;
+
+    @Autowired
+    public CarService(CarRepository carRepository, DriverRepository driverRepository,
+                      CarComponentRepository carComponentRepository, WebClient.Builder webClientBuilder) {
+        super(carRepository, Car.class);
+        this.driverRepository = driverRepository;
+        this.carRepository = carRepository;
+        this.carComponentRepository = carComponentRepository;
+
+        //if running in docker, modules cannot communicate through localhost
+        if (System.getenv("DOCKER") != null) {
+            VISUALIZATION_MODULE_URL = VISUALIZATION_MODULE_DOCKER;
+        }
+        else {
+            VISUALIZATION_MODULE_URL = VISUALIZATION_MODULE_LOCALHOST;
+        }
+        this.webClient = webClientBuilder.baseUrl(VISUALIZATION_MODULE_URL).build();
+    }
+
+    public Car create(List<Long> componentIds) {
+        var car = new Car();
+        createComponentsForCar(car, componentIds);
+        return car;
+    }
+
+    @Override
+    public void delete(Long id) {
+        Optional<Car> carFromDb = carRepository.findById(id);
+        if (carFromDb.isEmpty()) {
+            throw new ResourceNotFoundException(entityClass, id);
+        }
+
+        Driver driver = carFromDb.get().getDriver();
+        if (driver != null) {
+            Optional<Driver> driverFromDb = driverRepository.findById(driver.getId());
+            driverFromDb.get().setCar(null);
+            driverRepository.save(driverFromDb.get());
+        }
+
+        repository.delete(carFromDb.get());
+    }
+
+    public Car update(Long id, List<Long> componentIds) {
+        Optional<Car> dbCar = carRepository.findById(id);
+        if (dbCar.isEmpty()) {
+            throw new ResourceNotFoundException(entityClass, id);
+        }
+
+        createComponentsForCar(dbCar.get(), componentIds);
+
+        return dbCar.get();
+    }
+
+    @Transactional(readOnly = true)
+    public Car findById(Long id) {
+        return carRepository.findById(id)
+                .orElseThrow(() ->
+                        new ResourceNotFoundException(entityClass, id));
+    }
+
+    @Transactional(readOnly = true)
+    public Car get(Long id) {
+        Car car = findById(id);
+
+        try {
+            sendPostRequest(car);
+        } catch (WebClientException e) {
+            log.debug(String.format("The visualization module is not reachable on the URL: %s, exception %s", VISUALIZATION_MODULE_URL + VISUALIZATION_MODULE_URI, e));
+        }
+
+        return car;
+    }
+
+    public Car setDriver(Long carId, Long driverId) {
+        Optional<Driver> driverFromDb = driverRepository.findById(driverId);
+        if (driverFromDb.isEmpty()) {
+            throw new ResourceNotFoundException(Driver.class, driverId);
+        }
+
+        if (driverFromDb.get().getCar() != null) {
+            throw new BadRequestException("Driver is already driver of a different car");
+        }
+
+        Optional<Car> carFromDb = carRepository.findById(carId);
+        if (carFromDb.isEmpty()) {
+            throw new ResourceNotFoundException(entityClass, carId);
+        }
+
+        if (carFromDb.get().getDriver() != null) {
+            throw new BadRequestException("Car already has a driver");
+        }
+
+        carFromDb.get().setDriver(driverFromDb.get());
+
+        var savedCar = repository.save(carFromDb.get());
+
+        try {
+            sendPostRequest(savedCar);
+        } catch (WebClientException e) {
+            log.debug(String.format("The visualization module is not reachable on the URL: %s, exception %s", VISUALIZATION_MODULE_URL + VISUALIZATION_MODULE_URI, e));
+        }
+
+        return savedCar;
+    }
+
+    public Car removeDriver(Long carId) {
+        Optional<Car> carFromDb = carRepository.findById(carId);
+        if (carFromDb.isEmpty()) {
+            throw new ResourceNotFoundException(entityClass, carId);
+        }
+
+        Driver driver = carFromDb.get().getDriver();
+        if (driver == null) {
+            throw new BadRequestException(String.format("Car with id %s does not have a driver", carId));
+        }
+
+        Optional<Driver> driverFromDb = driverRepository.findById(driver.getId());
+        if (driverFromDb.isEmpty()) {
+            throw new ResourceNotFoundException(Driver.class, driver.getId());
+        }
+
+        carFromDb.get().setDriver(null);
+        driverFromDb.get().setCar(null);
+
+        driverRepository.save(driverFromDb.get());
+
+        return repository.save(carFromDb.get());
+    }
+
+    public Page<Car> findAll(Integer page, Integer size) {
+        Pageable pageable = PageRequest.of(page, size);
+
+        return repository.findAll(pageable);
+    }
+
+    private void createComponentsForCar(Car car, List<Long> componentIds) {
+        if (componentIds == null) {
+            repository.save(car);
+            return;
+        }
+
+        var carComponents = car.getComponents();
+
+        for (Long componentId : componentIds) {
+            Optional<CarComponent> dbComponent = carComponentRepository.findById(componentId);
+
+            if (dbComponent.isEmpty()) {
+                throw new ResourceNotFoundException(CarComponent.class, componentId);
+            }
+
+            if (carComponents.contains(dbComponent.get())) {
+                throw new BadRequestException("Car can only have one component of each type");
+            }
+
+            carComponents.add(dbComponent.get());
+            dbComponent.get().setCar(car);
+
+            repository.save(car);
+        }
+    }
+
+    public void sendPostRequest(Car car) throws RestClientException {
+        Map<String, Object> visualization = new HashMap<>();
+        visualization.put("car", car);
+
+        webClient.post()
+                .uri(VISUALIZATION_MODULE_URI)
+                .contentType(MediaType.APPLICATION_JSON)
+                .bodyValue(visualization)
+                .retrieve()
+                .bodyToMono(Resource.class)
+                .block();
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/service/DBManagementService.java b/core/src/main/java/cz/muni/pa165/service/DBManagementService.java
new file mode 100644
index 0000000000000000000000000000000000000000..0f1cd0062e2a560dd2826330a12f0d2d608453d4
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/service/DBManagementService.java
@@ -0,0 +1,150 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import cz.muni.pa165.data.model.*;
+import cz.muni.pa165.data.repository.*;
+import jakarta.annotation.PostConstruct;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+import java.util.Set;
+
+import static cz.muni.pa165.data.enums.CharacteristicsEnum.*;
+
+/**
+ * @author Michal Badin
+ */
+@Service
+@Profile("dev")
+public class DBManagementService {
+    private final CarComponentRepository carComponentRepository;
+    private final CarRepository carRepository;
+    private final DepartmentRepository departmentRepository;
+    private final DriverRepository driverRepository;
+    private final EngineerRepository engineerRepository;
+
+    @Autowired
+    public DBManagementService(CarComponentRepository carComponentRepository,
+                               CarRepository carRepository,
+                               DepartmentRepository departmentRepository,
+                               DriverRepository driverRepository,
+                               EngineerRepository engineerRepository) {
+        this.carComponentRepository = carComponentRepository;
+        this.carRepository = carRepository;
+        this.departmentRepository = departmentRepository;
+        this.driverRepository = driverRepository;
+        this.engineerRepository = engineerRepository;
+    }
+
+
+    @Transactional
+    @PostConstruct
+    public void seed() {
+        //region First car
+        Car car = new Car();
+
+        Driver driver = new Driver();
+        driver.setName("Xiao");
+        driver.setSurname("Chen");
+        driver.setBirthday(LocalDate.of(1995, 7, 29));
+        driver.setHeight(189);
+        driver.setNationality("Slovak");
+        driver.setCharacteristics(Set.of(AGGRESSIVENESS, EXCELLENT_STARTER, EXCELLENT_IN_THE_CORNERS, REACT_QUICKLY));
+        driverRepository.save(driver);
+
+        car.setDriver(driver);
+        carRepository.save(car);
+
+        saveCarComponent(ComponentTypeEnum.FRONTWING, 50.5, "Lightweight front wing v2 (black)", car);
+        saveCarComponent(ComponentTypeEnum.CHASSIS, 365.9, "Lightweight chassis", car);
+        saveCarComponent(ComponentTypeEnum.ENGINE, 300.5, "Lightweight V10 engine", car);
+        saveCarComponent(ComponentTypeEnum.SUSPENSION, 5.0, "Lightweight suspension v3 (black)", car);
+        //endregion
+
+        //region Second car
+        car = new Car();
+
+        driver = new Driver();
+        driver.setName("Marek");
+        driver.setSurname("Pilkovic");
+        driver.setBirthday(LocalDate.of(1987, 1, 1));
+        driver.setHeight(189);
+        driver.setNationality("British");
+        driver.setCharacteristics(Set.of(AGGRESSIVENESS, DRIVING_ON_THE_WET, EXCELLENT_IN_THE_CORNERS, REACT_QUICKLY));
+        driverRepository.save(driver);
+
+        car.setDriver(driver);
+        carRepository.save(car);
+
+        saveCarComponent(ComponentTypeEnum.FRONTWING, 57.5, "Lightweight front wing v3 (black)", car);
+        saveCarComponent(ComponentTypeEnum.CHASSIS, 325.9, "Lightweight chassis v5", car);
+        saveCarComponent(ComponentTypeEnum.ENGINE, 500.5, "V18 engine", car);
+        saveCarComponent(ComponentTypeEnum.SUSPENSION, 6.5, "Lightweight suspension v5 (white)", car);
+        //endregion
+
+        //region Test driver
+        Driver testDriver = new Driver();
+        testDriver.setName("Anuja");
+        testDriver.setSurname("Ankska");
+        testDriver.setNationality("Czech");
+        testDriver.setBirthday(LocalDate.of(2000, 1, 30));
+        testDriver.setHeight(175);
+        testDriver.setCharacteristics(Set.of(DRIVING_ON_THE_WET, REMAIN_COMPLETELY_FOCUSED, EFFICIENT_CARDIOVASCULAR_SYSTEMS));
+        driverRepository.save(testDriver);
+        //endregion
+
+        //region Car components
+        saveCarComponent(ComponentTypeEnum.FRONTWING, 70.5, "Front wing v7 (red)", null);
+        saveCarComponent(ComponentTypeEnum.CHASSIS, 500.9, "Medium weight chassis v2", null);
+        saveCarComponent(ComponentTypeEnum.ENGINE, 581.5, "V12 engine", null);
+        saveCarComponent(ComponentTypeEnum.SUSPENSION, 9.5, "Lightweight suspension v5 (white)", null);
+        //endregion
+
+        //region Department and engineers
+        Department department = new Department();
+        department.setSpecialization("Engine department");
+        departmentRepository.save(department);
+
+        Engineer engineer1 = new Engineer();
+        engineer1.setName("Michal");
+        engineer1.setSurname("Pekarik");
+        engineer1.setDepartment(department);
+        engineerRepository.save(engineer1);
+
+        Engineer engineer2 = new Engineer();
+        engineer2.setName("Andrej");
+        engineer2.setSurname("Uzasny");
+        engineer2.setDepartment(department);
+        engineerRepository.save(engineer2);
+
+        Engineer engineer3 = new Engineer();
+        engineer3.setName("Jitka");
+        engineer3.setSurname("Dlouha");
+        engineer3.setDepartment(department);
+        engineerRepository.save(engineer3);
+
+        Engineer engineer4 = new Engineer();
+        engineer4.setName("Betka");
+        engineer4.setSurname("Muslova");
+        engineer4.setDepartment(department);
+        engineerRepository.save(engineer4);
+        //endregion
+    }
+
+    private void saveCarComponent(ComponentTypeEnum type, double weight, String information, Car car) {
+        CarComponent carComponentFrontWing = new CarComponent(type, weight, information, car);
+        carComponentRepository.save(carComponentFrontWing);
+    }
+
+    @Transactional
+    public void clear() {
+        carComponentRepository.deleteAll();
+        carRepository.deleteAll();
+        driverRepository.deleteAll();
+        departmentRepository.deleteAll();
+        engineerRepository.deleteAll();
+    }
+}
diff --git a/core/src/main/java/cz/muni/pa165/service/DepartmentService.java b/core/src/main/java/cz/muni/pa165/service/DepartmentService.java
new file mode 100644
index 0000000000000000000000000000000000000000..707cf1d30221c816c55caae5b0d0f82995a188ea
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/service/DepartmentService.java
@@ -0,0 +1,66 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.model.Department;
+import cz.muni.pa165.data.model.Engineer;
+import cz.muni.pa165.data.repository.DepartmentRepository;
+import cz.muni.pa165.data.repository.EngineerRepository;
+import cz.muni.pa165.exceptions.BadRequestException;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class DepartmentService extends DomainService<Department> {
+
+    private final DepartmentRepository departmentRepository;
+    private final EngineerRepository engineerRepository;
+
+    @Autowired
+    public DepartmentService(DepartmentRepository departmentRepository, EngineerRepository engineerRepository) {
+        super(departmentRepository, Department.class);
+        this.departmentRepository = departmentRepository;
+        this.engineerRepository = engineerRepository;
+    }
+
+    @Transactional(readOnly = true)
+    public Department findById(Long id) {
+        return departmentRepository.findById(id)
+                .orElseThrow(() ->
+                        new ResourceNotFoundException(entityClass, id));
+    }
+
+    public Department addEngineer(Long departmentId, Long engineerId) {
+        Department department = findById(departmentId);
+
+        Optional<Engineer> engineer = engineerRepository.findById(engineerId);
+        if (engineer.isEmpty()) {
+            throw new ResourceNotFoundException(entityClass, engineerId);
+        }
+
+        if (department.getEngineers().contains(engineer.get())) {
+            throw new BadRequestException("Engineer already added to department");
+        }
+
+        department.addEngineer(engineer.get());
+        engineer.get().setDepartment(department);
+
+        return departmentRepository.save(department);
+    }
+
+    public Department updateSpecialization(Long id, Department department) {
+        Department departmentFromDb = findById(id);
+        departmentFromDb.setSpecialization(department.getSpecialization());
+
+        return departmentRepository.save(departmentFromDb);
+    }
+
+    @Transactional(readOnly = true)
+    public List<Department> findAll() {
+        return departmentRepository.findAll();
+    }
+
+}
diff --git a/core/src/main/java/cz/muni/pa165/service/DomainService.java b/core/src/main/java/cz/muni/pa165/service/DomainService.java
new file mode 100644
index 0000000000000000000000000000000000000000..c290f6e0fc87edd9b07e512236e598bf8fe4e70b
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/service/DomainService.java
@@ -0,0 +1,40 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.model.DomainObject;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Michal Badin
+ */
+public abstract class DomainService<ENTITY extends DomainObject> {
+    protected final JpaRepository<ENTITY, Long> repository;
+    protected final Class<ENTITY> entityClass;
+
+    protected DomainService(JpaRepository<ENTITY, Long> repository, Class<ENTITY> entityClass) {
+        this.repository = repository;
+        this.entityClass = entityClass;
+    }
+
+    public ENTITY create(ENTITY entity) {
+        return repository.save(entity);
+    }
+
+    public void delete(Long id) {
+        Optional<ENTITY> fromDb = repository.findById(id);
+        if (fromDb.isEmpty()) {
+            throw new ResourceNotFoundException(entityClass, id);
+        }
+        repository.delete(fromDb.get());
+    }
+
+    @Transactional(readOnly = true)
+    public List<ENTITY> findAll() {
+        return repository.findAll();
+    }
+
+}
diff --git a/core/src/main/java/cz/muni/pa165/service/DriverService.java b/core/src/main/java/cz/muni/pa165/service/DriverService.java
new file mode 100644
index 0000000000000000000000000000000000000000..67740ec8e6c16f12dc84dc81b8fb76d3c70a6b99
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/service/DriverService.java
@@ -0,0 +1,73 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.model.Driver;
+import cz.muni.pa165.data.repository.DriverRepository;
+import cz.muni.pa165.exceptions.BadRequestException;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Michal Badin
+ */
+@Service
+public class DriverService extends DomainService<Driver> {
+
+    private final DriverRepository driverRepository;
+
+    @Autowired
+    public DriverService(DriverRepository repository) {
+        super(repository, Driver.class);
+        driverRepository = repository;
+    }
+
+    @Transactional(readOnly = true)
+    public Driver findById(Long id) {
+        Optional<Driver> dbDriver = driverRepository.findById(id);
+        if (dbDriver.isEmpty()) {
+            throw new ResourceNotFoundException(entityClass, id);
+        }
+
+        return dbDriver.get();
+    }
+
+    public Driver update(Long id, Driver driver) {
+        Optional<Driver> dbDriver = driverRepository.findById(id);
+        if (dbDriver.isEmpty()) {
+            throw new ResourceNotFoundException(entityClass, id);
+        }
+
+        dbDriver.get().setName(driver.getName());
+        dbDriver.get().setSurname(driver.getSurname());
+        dbDriver.get().setNationality(driver.getNationality());
+        dbDriver.get().setHeight(driver.getHeight());
+        dbDriver.get().setCharacteristics(driver.getCharacteristics());
+        return driverRepository.save(dbDriver.get());
+    }
+
+    @Transactional(readOnly = true)
+    public List<Driver> findAllTestDrivers() {
+        return driverRepository.findAllTestDrivers();
+    }
+
+    @Override
+    public void delete(Long id) {
+        var driverFromDb = repository.findById(id);
+        if (driverFromDb.isEmpty()) {
+            throw new ResourceNotFoundException(entityClass, id);
+        }
+
+        if (driverFromDb.get().getCar() != null) {
+            throw new BadRequestException(
+                    String.format("Driver with id %s is driver of the car with id %s. Remove the driver from the car first.",
+                            id, driverFromDb.get().getCar().getId()));
+        }
+
+        repository.delete(driverFromDb.get());
+    }
+}
+
diff --git a/core/src/main/java/cz/muni/pa165/service/EngineerService.java b/core/src/main/java/cz/muni/pa165/service/EngineerService.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ea467cf5e3b6266f032984b14919e105f15319b
--- /dev/null
+++ b/core/src/main/java/cz/muni/pa165/service/EngineerService.java
@@ -0,0 +1,26 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.model.Engineer;
+import cz.muni.pa165.data.repository.EngineerRepository;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+
+@Service
+public class EngineerService extends DomainService<Engineer> {
+    private final EngineerRepository engineerRepository;
+
+    protected EngineerService(EngineerRepository repository) {
+        super(repository, Engineer.class);
+        this.engineerRepository = repository;
+    }
+
+    @Transactional(readOnly = true)
+    public Engineer findById(Long id) {
+        return engineerRepository.findById(id)
+                .orElseThrow(() ->
+                        new ResourceNotFoundException(entityClass, id));
+    }
+
+}
diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties
new file mode 100644
index 0000000000000000000000000000000000000000..f2c74faaa1f71d839634e9d836e4c670ba93150f
--- /dev/null
+++ b/core/src/main/resources/application.properties
@@ -0,0 +1,40 @@
+logging.level.root=info
+logging.level.cz.muni=debug
+server.port=8090
+spring.jpa.open-in-view=false
+spring.datasource.url=jdbc:h2:mem:formula-core;MODE=PostgreSQL
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=formulky
+spring.datasource.password=123456
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.jpa.show-sql=true
+spring.jackson.property-naming-strategy=SNAKE_CASE
+spring.cache.type=NONE
+spring.profiles.active=dev
+appconfig.enablecache=false
+
+#resource server nastavenia
+spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://oidc.muni.cz/oidc/introspect
+spring.security.oauth2.resourceserver.opaquetoken.client-id=d57b3a8f-156e-46de-9f27-39c4daee05e1
+spring.security.oauth2.resourceserver.opaquetoken.client-secret=fa228ebc-4d54-4cda-901e-4d6287f8b1652a9c9c44-73c9-4502-973f-bcdb4a8ec96a
+
+#logovanie security do console
+logging.level.org.springframework.security=info
+
+#swagger nastavenia
+springdoc.swagger-ui.oauth.client-id=7e02a0a9-446a-412d-ad2b-90add47b0fdd
+springdoc.swagger-ui.oauth.client-secret=48a2b2e3-4b2b-471e-b7b7-b81a85b6eeef22f347f2-3fc9-4e16-8698-3e2492701a89
+springdoc.swagger-ui.oauth.scopes=openid, test_1, test_5
+
+management.endpoints.web.exposure.include=info,health,metrics,loggers,beans,env,prometheus
+management.endpoint.health.show-details=always
+management.endpoint.health.show-components=always
+management.endpoint.health.probes.enabled=true
+
+management.info.env.enabled=true
+info.app.encoding=UTF-8
+info.app.java.source=17
+info.app.java.target=17
+
+#notification receivers
+notification.receivers=formula.team.management@gmail.com
\ No newline at end of file
diff --git a/core/src/test/java/cz/muni/pa165/CoreIT.java b/core/src/test/java/cz/muni/pa165/CoreIT.java
new file mode 100644
index 0000000000000000000000000000000000000000..d0d03ea84d6cdf644b6e654b999c81874e9e814f
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/CoreIT.java
@@ -0,0 +1,344 @@
+package cz.muni.pa165;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import cz.muni.pa165.data.model.*;
+import cz.muni.pa165.generated.core.model.*;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Integration tests. Run by "mvn verify / mvn test / mvn clean install".
+ * Testing functionality of the module as a whole.
+ */
+@SpringBootTest
+@AutoConfigureMockMvc(addFilters = false)
+@ActiveProfiles("test")
+class CoreIT {
+
+    private static final Logger log = LoggerFactory.getLogger(CoreIT.class);
+    private final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @Test
+    void testCreateCar() throws Exception {
+        log.info("testCreateCar() running");
+
+        CarDto carDto = new CarDto();
+        String requestBody = mapper.writeValueAsString(carDto);
+
+        String response = mockMvc.perform(
+                        post("/car")
+                                .accept(MediaType.APPLICATION_JSON)
+                                .contentType(MediaType.APPLICATION_JSON)
+                                .content(requestBody))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+        log.info("response: {}", response);
+
+        Car received = mapper.readValue(response, Car.class);
+        Assertions.assertNotNull(received);
+    }
+
+    @Test
+    void testCreateComponent() throws Exception {
+        log.info("testCreateComponent() running");
+
+        CarComponentDto componentDto = new CarComponentDto()
+                .componentType(CarComponentType.CHASSIS)
+                .weight(BigDecimal.valueOf(50.5))
+                .information("lightweight front wing v2 (black)");
+        String requestBody = mapper.writeValueAsString(componentDto);
+
+        String response = mockMvc.perform(
+                        post("/carComponent")
+                                .accept(MediaType.APPLICATION_JSON)
+                                .contentType(MediaType.APPLICATION_JSON)
+                                .content(requestBody))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+
+        CarComponent received = mapper.readValue(response, CarComponent.class);
+        Assertions.assertEquals(ComponentTypeEnum.CHASSIS, received.getComponentType());
+        Assertions.assertEquals(50.5, received.getWeight());
+        Assertions.assertEquals("lightweight front wing v2 (black)", received.getInformation());
+    }
+
+    @Test
+    void testCreateComponentWrongData() throws Exception {
+        log.info("testCreateComponentWrongData() running");
+
+        CarComponentDto componentDto = new CarComponentDto()
+                .componentType(CarComponentType.CHASSIS)
+                .weight(BigDecimal.valueOf(-5))
+                .information("lightweight front wing v2 (black)");
+        String requestBody = mapper.writeValueAsString(componentDto);
+
+        mockMvc.perform(
+                        post("/carComponent")
+                                .accept(MediaType.APPLICATION_JSON)
+                                .contentType(MediaType.APPLICATION_JSON)
+                                .content(requestBody))
+                .andExpect(status().is5xxServerError());
+    }
+
+    @Test
+    void testCreateDriver() throws Exception {
+        log.info("testCreateDriver running");
+
+        DriverDto driverDto = new DriverDto()
+                .name("John")
+                .surname("Doe")
+                .nationality("Italian")
+                .height(189)
+                .birthday(LocalDate.of(1990, 10, 1))
+                .addCharacteristicsItem(Characteristic.AGGRESSIVENESS);
+        String requestBody = mapper.writeValueAsString(driverDto);
+
+        String response = mockMvc.perform(
+                        post("/driver")
+                                .accept(MediaType.APPLICATION_JSON)
+                                .contentType(MediaType.APPLICATION_JSON)
+                                .content(requestBody))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+        log.info("response: {}", response);
+
+        Driver received = mapper.readValue(response, Driver.class);
+        Assertions.assertEquals("John", received.getName());
+        Assertions.assertEquals("Doe", received.getSurname());
+        Assertions.assertEquals("Italian", received.getNationality());
+        Assertions.assertEquals(189, received.getHeight());
+        Assertions.assertEquals(LocalDate.of(1990, 10, 1), received.getBirthday());
+    }
+
+    @Test
+    void testCreateDriverWrongData() throws Exception {
+        log.info("testCreateDriverWrongData running");
+
+        DriverDto driverDto = new DriverDto()
+                .name("John")
+                .surname("Doe")
+                .nationality("Italian")
+                .height(50)
+                .birthday(LocalDate.of(2999, 10, 1))
+                .addCharacteristicsItem(Characteristic.AGGRESSIVENESS);
+        String requestBody = mapper.writeValueAsString(driverDto);
+
+        mockMvc.perform(
+                        post("/driver")
+                                .accept(MediaType.APPLICATION_JSON)
+                                .contentType(MediaType.APPLICATION_JSON)
+                                .content(requestBody))
+                .andExpect(status().is5xxServerError());
+    }
+
+    @Test
+    void testCreateDepartment() throws Exception {
+        log.info("testCreateDepartment() running");
+
+        DepartmentDto departmentDto = new DepartmentDto().specialization("aero");
+        String requestBody = mapper.writeValueAsString(departmentDto);
+
+        String response = mockMvc.perform(
+                        post("/department")
+                                .accept(MediaType.APPLICATION_JSON)
+                                .contentType(MediaType.APPLICATION_JSON)
+                                .content(requestBody))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+        log.info("response: {}", response);
+
+        Department received = mapper.readValue(response, Department.class);
+        Assertions.assertEquals("aero", received.getSpecialization());
+    }
+
+    @Test
+    void testCreateDepartmentWrongData() throws Exception {
+        log.info("testCreateDepartmentWrongData() running");
+
+        DepartmentDto departmentDto = new DepartmentDto().specialization("");
+        String requestBody = mapper.writeValueAsString(departmentDto);
+
+        mockMvc.perform(
+                        post("/department")
+                                .accept(MediaType.APPLICATION_JSON)
+                                .contentType(MediaType.APPLICATION_JSON)
+                                .content(requestBody))
+                .andExpect(status().is5xxServerError());
+    }
+
+    @Test
+    void testComplexScenario1() throws Exception {
+        log.info("testComplexScenario1() running");
+
+        Car car = loadEmptyCar();
+        Driver driver = loadDriver();
+        List<CarComponent> components = loadComponents();
+
+        String carWithDriverResponse = mockMvc.perform(
+                        put("/car/driver?carId=" + car.getId() + "&driverId=" + driver.getId())
+                                .accept(MediaType.APPLICATION_JSON))
+                .andReturn().getResponse().getContentAsString();
+
+        Car carWithDriver = mapper.readValue(carWithDriverResponse, Car.class);
+        car.setDriver(driver);
+        Assertions.assertEquals(car, carWithDriver);
+
+        car.setComponents(new HashSet<>(components));
+        String carUpdateRequestQuery = components.stream().map(CarComponent::getId).map(String::valueOf).collect(Collectors.joining("&componentIds="));
+        String carWithComponentsResponse = mockMvc.perform(
+                        patch("/car/" + car.getId() + "?componentIds=" + carUpdateRequestQuery)
+                                .accept(MediaType.APPLICATION_JSON))
+                .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
+
+        Car carWithComponents = mapper.readValue(carWithComponentsResponse, Car.class);
+        Assertions.assertEquals(car, carWithComponents);
+
+        mockMvc.perform(
+                        delete("/car/driver?carId=" + car.getId()))
+                .andExpect(status().isOk());
+
+        String findCarDeletedDriver = mockMvc.perform(
+                get("/car/" + car.getId())).andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
+
+        Car carDeletedDriver = mapper.readValue(findCarDeletedDriver, Car.class);
+        Assertions.assertNull(carDeletedDriver.getDriver());
+
+        mockMvc.perform(delete("/car/" + car.getId())).andExpect(status().isOk());
+
+        mockMvc.perform(get("/car/" + car.getId())).andExpect(status().isNotFound());
+    }
+
+    @Test
+    void testComplexScenario2() throws Exception {
+        log.info("testComplexScenario1() running");
+
+        Department department = loadEmptyDepartment();
+        Engineer engineer = loadEngineer();
+
+        String departmentWithEngineerResponse = mockMvc.perform(
+                        put("/department/" + department.getId() + "?engineerId=" + engineer.getId())
+                                .accept(MediaType.APPLICATION_JSON))
+                .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
+
+        Department departmentWithEngineer = mapper.readValue(departmentWithEngineerResponse, Department.class);
+
+        department.addEngineer(engineer);
+        Assertions.assertEquals(1, departmentWithEngineer.getEngineers().size());
+        Assertions.assertEquals(department, departmentWithEngineer);
+
+        mockMvc.perform(delete("/department/" + department.getId())).andExpect(status().isOk());
+
+        mockMvc.perform(get("/department/" + department.getId())).andExpect(status().isNotFound());
+    }
+
+    private Department loadEmptyDepartment() throws Exception {
+        DepartmentDto departmentRequest = new DepartmentDto().specialization("aero").engineers(new ArrayList<>());
+        String departmentRequestBody = mapper.writeValueAsString(departmentRequest);
+        String departmentResponse = mockMvc.perform(post("/department")
+                        .accept(MediaType.APPLICATION_JSON)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(departmentRequestBody))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+        Department received = mapper.readValue(departmentResponse, Department.class);
+        received.setEngineers(new HashSet<>());
+        return received;
+    }
+
+    private Engineer loadEngineer() throws Exception {
+        EngineerDto engineerRequest = new EngineerDto().name("John").surname("Doe");
+        String engineerRequestBody = mapper.writeValueAsString(engineerRequest);
+        String engineerResponse = mockMvc.perform(post("/engineer")
+                        .accept(MediaType.APPLICATION_JSON)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(engineerRequestBody))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+        return mapper.readValue(engineerResponse, Engineer.class);
+    }
+
+    private Car loadEmptyCar() throws Exception {
+        CarDto carRequest = new CarDto();
+        String carRequestBody = mapper.writeValueAsString(carRequest);
+        String carResponse = mockMvc.perform(post("/car")
+                        .accept(MediaType.APPLICATION_JSON)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(carRequestBody))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+        return mapper.readValue(carResponse, Car.class);
+    }
+
+    private Driver loadDriver() throws Exception {
+        DriverDto driverRequest = new DriverDto()
+                .name("John")
+                .surname("Doe")
+                .nationality("Italian")
+                .height(189)
+                .birthday(LocalDate.of(1990, 10, 1))
+                .addCharacteristicsItem(Characteristic.AGGRESSIVENESS);
+        String driverRequestBody = mapper.writeValueAsString(driverRequest);
+
+        String driverResponse = mockMvc.perform(post("/driver")
+                        .accept(MediaType.APPLICATION_JSON)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(driverRequestBody))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+
+        return mapper.readValue(driverResponse, Driver.class);
+    }
+
+    private List<CarComponent> loadComponents() throws Exception {
+        CarComponentDto componentRequestChassis = new CarComponentDto()
+                .componentType(CarComponentType.CHASSIS)
+                .weight(BigDecimal.valueOf(50.5))
+                .information("new aerodynamic chassis");
+        CarComponentDto componentRequestEngine = new CarComponentDto()
+                .componentType(CarComponentType.ENGINE)
+                .weight(BigDecimal.valueOf(100))
+                .information("honda engine v2 500HP");
+        String componentRequestBodyChassis = mapper.writeValueAsString(componentRequestChassis);
+        String componentRequestBodyEngine = mapper.writeValueAsString(componentRequestEngine);
+        String componentResponseChassis = mockMvc.perform(post("/carComponent")
+                        .accept(MediaType.APPLICATION_JSON)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(componentRequestBodyChassis))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+        String componentResponseEngine = mockMvc.perform(post("/carComponent")
+                        .accept(MediaType.APPLICATION_JSON)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(componentRequestBodyEngine))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+        CarComponent componentChassis = mapper.readValue(componentResponseChassis, CarComponent.class);
+        CarComponent componentEngine = mapper.readValue(componentResponseEngine, CarComponent.class);
+        return List.of(componentChassis, componentEngine);
+    }
+
+}
+
diff --git a/core/src/test/java/cz/muni/pa165/data/repository/CarComponentRepositoryTest.java b/core/src/test/java/cz/muni/pa165/data/repository/CarComponentRepositoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..73b79b367089b0b7c6a901210ef9198060274244
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/data/repository/CarComponentRepositoryTest.java
@@ -0,0 +1,127 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import cz.muni.pa165.data.model.Car;
+import cz.muni.pa165.data.model.CarComponent;
+import jakarta.persistence.EntityManager;
+import jakarta.transaction.Transactional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SpringBootTest
+@Transactional
+@ActiveProfiles("test")
+class CarComponentRepositoryTest {
+
+    @Autowired
+    private CarComponentRepository carComponentRepository;
+
+    @Autowired
+    private EntityManager entityManager;
+
+    private CarComponent carComponent;
+    private CarComponent carComponentWithoutCar;
+
+    @BeforeEach
+    void setUp() {
+        Car car = new Car();
+        entityManager.persist(car);
+
+        carComponent = new CarComponent(
+                ComponentTypeEnum.CHASSIS,
+                180.0,
+                "Dummy information",
+                car
+        );
+        entityManager.persist(carComponent);
+        car.setComponents(Set.of(carComponent));
+
+        carComponentWithoutCar = new CarComponent(
+                ComponentTypeEnum.ENGINE,
+                180.0,
+                "Dummy information",
+                null
+        );
+        entityManager.persist(carComponentWithoutCar);
+
+        entityManager.flush();
+    }
+
+    @Test
+    void findExistingById() {
+        Optional<CarComponent> carComponentFetched = carComponentRepository.findById(carComponent.getId());
+
+        assertNotNull(carComponentFetched);
+        assertTrue(carComponentFetched.isPresent());
+
+        assertEquals(carComponent, carComponentFetched.get());
+    }
+
+    @Test
+    void findNonExistingById() {
+        Optional<CarComponent> carComponentFetched = carComponentRepository.findById(Long.MAX_VALUE);
+
+        assertNotNull(carComponentFetched);
+        assertTrue(carComponentFetched.isEmpty());
+    }
+
+    @Test
+    void findAll() {
+        List<CarComponent> carComponentList = carComponentRepository.findAll();
+
+        assertNotNull(carComponentList);
+        assertEquals(List.of(carComponent, carComponentWithoutCar), carComponentList);
+    }
+
+    @Test
+    void findByType() {
+        List<CarComponent> carComponentList = carComponentRepository.findByType(ComponentTypeEnum.ENGINE);
+
+        assertNotNull(carComponentList);
+        assertEquals(List.of(carComponentWithoutCar), carComponentList);
+    }
+
+    @Test
+    void delete() {
+        CarComponent carComponentToDelete = new CarComponent(ComponentTypeEnum.CHASSIS, 180.0, "Dummy information", null);
+        entityManager.persist(carComponentToDelete);
+
+        carComponentRepository.delete(carComponentToDelete);
+
+        entityManager.flush();
+
+        assertEquals(2, carComponentRepository.count());
+
+        CarComponent carComponentFetched = entityManager.find(CarComponent.class, carComponentToDelete.getId());
+        assertNull(carComponentFetched);
+
+        List<CarComponent> carComponentList = carComponentRepository.findAll();
+        assertNotNull(carComponentList);
+        assertEquals(List.of(carComponent, carComponentWithoutCar), carComponentList);
+    }
+
+    @Test
+    void deleteNonExisting() {
+        CarComponent carComponentToDelete = new CarComponent();
+        carComponentRepository.delete(carComponentToDelete);
+
+        Optional<CarComponent> carComponentFetched = carComponentRepository.findById(carComponentToDelete.getId());
+
+        assertNotNull(carComponentFetched);
+        assertTrue(carComponentFetched.isEmpty());
+
+        List<CarComponent> carComponentList = carComponentRepository.findAll();
+
+        assertNotNull(carComponentList);
+        assertEquals(List.of(carComponent, carComponentWithoutCar), carComponentList);
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/java/cz/muni/pa165/data/repository/CarRepositoryTest.java b/core/src/test/java/cz/muni/pa165/data/repository/CarRepositoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..51a9fcc9c68dcdfbddd242e02bc5dc1424dec9c9
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/data/repository/CarRepositoryTest.java
@@ -0,0 +1,112 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import cz.muni.pa165.data.model.Car;
+import cz.muni.pa165.data.model.CarComponent;
+import cz.muni.pa165.data.model.Driver;
+import jakarta.persistence.EntityManager;
+import jakarta.transaction.Transactional;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.time.LocalDate;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+
+/**
+ * @author Michal Badin
+ */
+@SpringBootTest
+@Transactional
+@ActiveProfiles("test")
+public class CarRepositoryTest {
+    @Autowired
+    private EntityManager entityManager;
+
+    @Autowired
+    private CarRepository carRepository;
+
+    private Car car;
+
+    @BeforeEach
+    void setUp() {
+        car = new Car();
+
+        Driver driver = new Driver("John", "Doe", 180, LocalDate.of(1990, 1, 1), "Czech", car, new HashSet<>());
+
+        CarComponent carComponent = new CarComponent(ComponentTypeEnum.CHASSIS, 180.0, "Dummy information", car);
+
+        car.setDriver(driver);
+        car.setComponents(Set.of(carComponent));
+
+        entityManager.persist(car);
+        entityManager.persist(carComponent);
+        entityManager.persist(driver);
+        entityManager.flush();
+    }
+
+    @Test
+    void testSave() {
+        Car carToSaved = new Car();
+
+        Car saved = carRepository.save(carToSaved);
+
+        assertNotNull(saved);
+        Assertions.assertEquals(carToSaved, saved);
+    }
+
+    @Test
+    void findByExistingId() {
+        Optional<Car> carFetched = carRepository.findById(car.getId());
+
+        assertNotNull(carFetched);
+        assertTrue(carFetched.isPresent());
+
+        assertEquals(car, carFetched.get());
+    }
+
+    @Test
+    void findByNonExistingId() {
+        Optional<Car> carFetched = carRepository.findById(Long.MAX_VALUE);
+
+        assertNotNull(carFetched);
+        assertTrue(carFetched.isEmpty());
+    }
+
+    @Test
+    void delete() {
+        Car carToDelete = new Car();
+        entityManager.persist(carToDelete);
+
+        carRepository.delete(carToDelete);
+
+        Optional<Car> carFetched = carRepository.findById(carToDelete.getId());
+
+        assertTrue(carFetched.isEmpty());
+    }
+
+    @Test
+    void deleteNonExisting() {
+        var carToDelete = new Car();
+        carRepository.delete(carToDelete);
+
+        Optional<Car> carFetched = carRepository.findById(carToDelete.getId());
+
+        assertNotNull(carFetched);
+        assertTrue(carFetched.isEmpty());
+
+        var carList = carRepository.findAll();
+
+        assertNotNull(carList);
+        assertEquals(List.of(car), carList);
+    }
+}
diff --git a/core/src/test/java/cz/muni/pa165/data/repository/DepartmentRepositoryTest.java b/core/src/test/java/cz/muni/pa165/data/repository/DepartmentRepositoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1cd42abba22ee810fdebdf46c1804a0805ff88ff
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/data/repository/DepartmentRepositoryTest.java
@@ -0,0 +1,101 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.model.Department;
+import cz.muni.pa165.data.model.Engineer;
+import jakarta.persistence.EntityManager;
+import jakarta.transaction.Transactional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SpringBootTest
+@Transactional
+@ActiveProfiles("test")
+class DepartmentRepositoryTest {
+
+    @Autowired
+    private DepartmentRepository departmentRepository;
+
+    @Autowired
+    private EntityManager entityManager;
+
+    private final Department department = new Department("dep1", new HashSet<>());
+    private final Department emptyDepartment = new Department("dep2", new HashSet<>());
+    private final Engineer engineer = new Engineer("John", "Doe", null);
+
+    @BeforeEach
+    void setUp() {
+        department.addEngineer(engineer);
+        entityManager.persist(engineer);
+        entityManager.persist(department);
+        entityManager.persist(emptyDepartment);
+        entityManager.flush();
+    }
+
+    @Test
+    void findExistingById() {
+        Optional<Department> departmentFetched = departmentRepository.findById(department.getId());
+
+        assertNotNull(departmentFetched);
+        assertTrue(departmentFetched.isPresent());
+
+        assertEquals(department, departmentFetched.get());
+    }
+
+    @Test
+    void findNonExistingById() {
+        Optional<Department> departmentFetched = departmentRepository.findById(Long.MAX_VALUE);
+
+        assertNotNull(departmentFetched);
+        assertTrue(departmentFetched.isEmpty());
+    }
+
+    @Test
+    void findAll() {
+        List<Department> departmentList = departmentRepository.findAll();
+
+        assertNotNull(departmentList);
+        assertEquals(List.of(department, emptyDepartment), departmentList);
+    }
+
+
+    @Test
+    void delete() {
+        departmentRepository.delete(department);
+
+        entityManager.flush();
+
+        assertEquals(1, departmentRepository.count());
+
+        Department departmentFetched = entityManager.find(Department.class, department.getId());
+        assertNull(departmentFetched);
+
+        List<Department> departmentList = departmentRepository.findAll();
+        assertNotNull(departmentList);
+        assertEquals(List.of(emptyDepartment), departmentList);
+    }
+
+    @Test
+    void deleteNonExisting() {
+        Department departmentToDelete = new Department("dep3", new HashSet<>());
+        departmentRepository.delete(departmentToDelete);
+
+        Optional<Department> departmentFetched = departmentRepository.findById(departmentToDelete.getId());
+
+        assertNotNull(departmentFetched);
+        assertTrue(departmentFetched.isEmpty());
+
+        List<Department> departmentList = departmentRepository.findAll();
+
+        assertNotNull(departmentList);
+        assertEquals(List.of(department, emptyDepartment), departmentList);
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/java/cz/muni/pa165/data/repository/DriverRepositoryTest.java b/core/src/test/java/cz/muni/pa165/data/repository/DriverRepositoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..20839082f2caf0d6b48aa2b190a2cafb2b80d3b5
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/data/repository/DriverRepositoryTest.java
@@ -0,0 +1,100 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.model.Car;
+import cz.muni.pa165.data.model.Driver;
+import jakarta.persistence.EntityManager;
+import jakarta.transaction.Transactional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SpringBootTest
+@Transactional
+@ActiveProfiles("test")
+class DriverRepositoryTest {
+
+    @Autowired
+    private DriverRepository driverRepository;
+
+    @Autowired
+    private EntityManager entityManager;
+
+    private Driver driverWithoutCar;
+    private Driver driverWithCar;
+
+    @BeforeEach
+    void setUp() {
+        driverWithoutCar = new Driver("jan", "novak", 175,
+                LocalDate.of(1990, 1, 1), "czech", null, null);
+        entityManager.persist(driverWithoutCar);
+
+        var car = new Car();
+        entityManager.persist(car);
+
+        driverWithCar = new Driver("jan", "novak", 175,
+                LocalDate.of(1990, 1, 1), "czech", car, null);
+        entityManager.persist(driverWithCar);
+        car.setDriver(driverWithCar);
+
+        entityManager.flush();
+    }
+
+    @Test
+    void findExistingById() {
+        Optional<Driver> driverFetched = driverRepository.findById(driverWithoutCar.getId());
+
+        assertNotNull(driverFetched);
+        assertTrue(driverFetched.isPresent());
+
+        assertEquals(driverWithoutCar, driverFetched.get());
+    }
+
+    @Test
+    void findNonExistingById() {
+        Optional<Driver> carComponentFetched = driverRepository.findById(Long.MAX_VALUE);
+
+        assertNotNull(carComponentFetched);
+        assertTrue(carComponentFetched.isEmpty());
+    }
+
+    @Test
+    void findAllTestDrivers() {
+        var testDrivers = driverRepository.findAllTestDrivers();
+
+        assertNotNull(testDrivers);
+        assertEquals(1, testDrivers.size());
+    }
+
+    @Test
+    void delete() {
+        driverRepository.delete(driverWithoutCar);
+
+        Optional<Driver> driverFetched = driverRepository.findById(driverWithoutCar.getId());
+
+        assertTrue(driverFetched.isEmpty());
+    }
+
+    @Test
+    void deleteNonExisting() {
+        var driverToDelete = new Driver();
+        driverRepository.delete(driverToDelete);
+
+        Optional<Driver> driverFetched = driverRepository.findById(driverToDelete.getId());
+
+        assertNotNull(driverFetched);
+        assertTrue(driverFetched.isEmpty());
+
+        var driverList = driverRepository.findAll();
+
+        assertNotNull(driverList);
+        assertEquals(List.of(driverWithCar, driverWithoutCar), driverList);
+    }
+}
diff --git a/core/src/test/java/cz/muni/pa165/data/repository/EngineerRepositoryTest.java b/core/src/test/java/cz/muni/pa165/data/repository/EngineerRepositoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..799b38d14461b6f7ccc97b95831aa2681b5d6926
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/data/repository/EngineerRepositoryTest.java
@@ -0,0 +1,101 @@
+package cz.muni.pa165.data.repository;
+
+import cz.muni.pa165.data.model.Department;
+import cz.muni.pa165.data.model.Engineer;
+import jakarta.persistence.EntityManager;
+import jakarta.transaction.Transactional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SpringBootTest
+@Transactional
+@ActiveProfiles("test")
+class EngineerRepositoryTest {
+
+    @Autowired
+    private EngineerRepository engineerRepository;
+
+    @Autowired
+    private EntityManager entityManager;
+
+    private final Department department = new Department("dep1", new HashSet<>());
+    private final Engineer engineer = new Engineer("Samantha", "Lee", null);
+    private final Engineer engineer2 = new Engineer("John", "Doe", department);
+
+    @BeforeEach
+    void setUp() {
+        department.addEngineer(engineer2);
+        entityManager.persist(engineer);
+        entityManager.persist(department);
+        entityManager.persist(engineer2);
+        entityManager.flush();
+    }
+
+    @Test
+    void findExistingById() {
+        Optional<Engineer> engineerFetched = engineerRepository.findById(engineer.getId());
+
+        assertNotNull(engineerFetched);
+        assertTrue(engineerFetched.isPresent());
+
+        assertEquals(engineer, engineerFetched.get());
+    }
+
+    @Test
+    void findNonExistingById() {
+        Optional<Engineer> engineerFetched = engineerRepository.findById(Long.MAX_VALUE);
+
+        assertNotNull(engineerFetched);
+        assertTrue(engineerFetched.isEmpty());
+    }
+
+    @Test
+    void findAll() {
+        List<Engineer> engineerList = engineerRepository.findAll();
+
+        assertNotNull(engineerList);
+        assertEquals(List.of(engineer, engineer2), engineerList);
+    }
+
+    @Test
+    void delete() {
+        engineerRepository.delete(engineer);
+
+        entityManager.flush();
+
+        assertEquals(1, engineerRepository.count());
+
+        Engineer engineerFetched = entityManager.find(Engineer.class, engineer.getId());
+        assertNull(engineerFetched);
+
+        List<Engineer> engineerList = engineerRepository.findAll();
+        assertNotNull(engineerList);
+        assertEquals(List.of(engineer2), engineerList);
+    }
+
+    @Test
+    void deleteNonExisting() {
+        Engineer nonExistingEngineer = new Engineer();
+        nonExistingEngineer.setId(999L);
+        engineerRepository.delete(nonExistingEngineer);
+
+        Optional<Engineer> nonExistingEngineerFetched = engineerRepository.findById(nonExistingEngineer.getId());
+
+        assertNotNull(nonExistingEngineerFetched);
+        assertTrue(nonExistingEngineerFetched.isEmpty());
+
+        List<Engineer> engineerList = engineerRepository.findAll();
+
+        assertNotNull(engineerList);
+        assertEquals(List.of(engineer, engineer2), engineerList);
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/java/cz/muni/pa165/rest/CarComponentControllerTest.java b/core/src/test/java/cz/muni/pa165/rest/CarComponentControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5373bb4a943ccd5e67348c00bb5211058fde2977
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/rest/CarComponentControllerTest.java
@@ -0,0 +1,34 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import cz.muni.pa165.facade.CarComponentFacade;
+import cz.muni.pa165.generated.core.model.CarComponentType;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class CarComponentControllerTest {
+    private static final Logger log = LoggerFactory.getLogger(CarComponentControllerTest.class);
+    private final CarComponentFacade mockFacade = Mockito.mock(CarComponentFacade.class);
+    private final CarComponentController carComponentController = new CarComponentController(mockFacade);
+
+    @Test
+    void testGetCarComponent() {
+        log.debug("starting testGetCarComponent()");
+
+        carComponentController.getCarComponent(1L);
+
+        Mockito.verify(mockFacade, Mockito.times(1)).findById(1L);
+    }
+
+    @Test
+    void testGetCarComponentsByType() {
+        log.debug("starting testGetCarComponentsByType()");
+
+        carComponentController.getCarComponentsByType(CarComponentType.CHASSIS);
+
+        Mockito.verify(mockFacade, Mockito.times(1)).findAllByType(ComponentTypeEnum.CHASSIS);
+    }
+}
+
diff --git a/core/src/test/java/cz/muni/pa165/rest/CarControllerTest.java b/core/src/test/java/cz/muni/pa165/rest/CarControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..653fba5eb44e40e1b108323edba8f8a40fe48a82
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/rest/CarControllerTest.java
@@ -0,0 +1,38 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.facade.CarFacade;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class CarControllerTest {
+    private static final Logger log = LoggerFactory.getLogger(CarControllerTest.class);
+
+    private final CarFacade mockFacade = Mockito.mock(CarFacade.class);
+    private final CarController controller = new CarController(mockFacade);
+
+    @Test
+    void testCreateCar() {
+        log.debug("starting testCreateCar()");
+
+        List<Long> components = new ArrayList<>();
+        components.add(1L);
+
+        controller.createCar(components);
+
+        Mockito.verify(mockFacade, Mockito.times(1)).create(components);
+    }
+
+    @Test
+    void testSetDriver() {
+        log.debug("starting testSetDriver()");
+
+        controller.setDriver(1L, 2L);
+
+        Mockito.verify(mockFacade, Mockito.times(1)).setDriver(1L, 2L);
+    }
+}
diff --git a/core/src/test/java/cz/muni/pa165/rest/DepartmentControllerTest.java b/core/src/test/java/cz/muni/pa165/rest/DepartmentControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2deea2d40ffabc1b2ecde2b35977ebf01e6986cc
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/rest/DepartmentControllerTest.java
@@ -0,0 +1,37 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.facade.DepartmentFacade;
+import cz.muni.pa165.generated.core.model.DepartmentUpdateDto;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class DepartmentControllerTest {
+
+    private static final Logger log = LoggerFactory.getLogger(DepartmentControllerTest.class);
+    private final DepartmentFacade mockFacade = Mockito.mock(DepartmentFacade.class);
+    private final DepartmentController controller = new DepartmentController(mockFacade);
+
+
+    @Test
+    void testAddEngineer() {
+        log.debug("starting testAddEngineer()");
+
+        controller.addEngineer(1L, 2L);
+
+        Mockito.verify(mockFacade, Mockito.times(1)).addEngineer(1L, 2L);
+    }
+
+    @Test
+    void testUpdateSpecialization() {
+        log.debug("starting testUpdateSpecialization()");
+
+        var updateDto = new DepartmentUpdateDto().specialization("test");
+        controller.updateSpecialization(1L, updateDto);
+
+        Mockito.verify(mockFacade, Mockito.times(1)).updateSpecialization(1L, updateDto);
+    }
+
+
+}
diff --git a/core/src/test/java/cz/muni/pa165/rest/DriverControllerTest.java b/core/src/test/java/cz/muni/pa165/rest/DriverControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2d9a28df609806d0285d0515d06e550b598a7bd
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/rest/DriverControllerTest.java
@@ -0,0 +1,27 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.facade.DriverFacade;
+import cz.muni.pa165.generated.core.model.Characteristic;
+import cz.muni.pa165.generated.core.model.DriverUpdateDto;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class DriverControllerTest {
+    private static final Logger log = LoggerFactory.getLogger(CarComponentControllerTest.class);
+    private final DriverFacade mockFacade = Mockito.mock(DriverFacade.class);
+    private final DriverController controller = new DriverController(mockFacade);
+
+    @Test
+    void updateDriver() {
+        log.debug("starting updateDriver()");
+
+        DriverUpdateDto updateDto = new DriverUpdateDto().addCharacteristicsItem(Characteristic.AGGRESSIVENESS);
+
+        controller.updateDriver(1L, updateDto);
+
+        Mockito.verify(mockFacade, Mockito.times(1)).update(1L, updateDto);
+    }
+
+}
diff --git a/core/src/test/java/cz/muni/pa165/service/CarComponentServiceTest.java b/core/src/test/java/cz/muni/pa165/service/CarComponentServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..de84639d90a6c1ea408c3286ae8a4c619225aeca
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/service/CarComponentServiceTest.java
@@ -0,0 +1,137 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.enums.ComponentTypeEnum;
+import cz.muni.pa165.data.model.Car;
+import cz.muni.pa165.data.model.CarComponent;
+import cz.muni.pa165.data.repository.CarComponentRepository;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.*;
+
+class CarComponentServiceTest {
+    private CarComponentService carComponentService;
+    private CarComponentRepository carComponentRepository;
+    private WebClient.Builder webClientBuilder;
+
+    @BeforeEach
+    void setUp() {
+        carComponentRepository = mock(CarComponentRepository.class);
+        webClientBuilder = WebClient.builder();
+        carComponentService = new CarComponentService(carComponentRepository, webClientBuilder);
+    }
+
+    @Test
+    void create() {
+        CarComponent carComponent = createFilledComponent(new Car());
+
+        when(carComponentRepository.save(carComponent)).thenReturn(carComponent);
+
+        assertEquals(carComponent, carComponentService.create(carComponent));
+        verify(carComponentRepository, times(1)).save(carComponent);
+    }
+
+    @Test
+    void updateExistingComponent() {
+        CarComponent carComponent = new CarComponent();
+        CarComponent carComponentForUpdate = createFilledComponent(new Car());
+
+        when(carComponentRepository.findById(carComponent.getId())).thenReturn(Optional.of(carComponent));
+        when(carComponentRepository.save(carComponent)).thenReturn(carComponent);
+
+        assertEquals(carComponentForUpdate, carComponentService.update(carComponent.getId(), carComponentForUpdate));
+        verify(carComponentRepository, times(1)).save(carComponent);
+    }
+
+    @Test
+    void updateNonExistingComponent() {
+        var exceptionToThrow = new ResourceNotFoundException(CarComponent.class, Long.MAX_VALUE);
+
+        when(carComponentRepository.findById(Long.MAX_VALUE)).thenReturn(Optional.empty());
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carComponentService.update(Long.MAX_VALUE, null);
+        });
+    }
+
+    @Test
+    void findExistingComponentById() {
+        CarComponent carComponent = createFilledComponent(new Car());
+
+        when(carComponentRepository.findById(carComponent.getId())).thenReturn(Optional.of(carComponent));
+
+        assertEquals(carComponent, carComponentService.findById(carComponent.getId()));
+    }
+
+    @Test
+    void findNonExistingComponentById() {
+        var exceptionToThrow = new ResourceNotFoundException(CarComponent.class, Long.MAX_VALUE);
+
+        when(carComponentRepository.findById(Long.MAX_VALUE)).thenReturn(Optional.empty());
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carComponentService.findById(Long.MAX_VALUE);
+        });
+    }
+
+    @Test
+    void findAll() {
+        List<CarComponent> carComponentList = List.of(
+                createFilledComponent(new Car()),
+                createFilledComponent(new Car()),
+                new CarComponent()
+        );
+
+        when(carComponentRepository.findAll()).thenReturn(carComponentList);
+
+        assertEquals(carComponentList, carComponentService.findAll());
+    }
+
+    @Test
+    void findAllByType() {
+        List<CarComponent> carComponentList = List.of(
+                createFilledComponent(new Car()),
+                createFilledComponent(new Car())
+        );
+
+        when(carComponentRepository.findByType(ComponentTypeEnum.CHASSIS)).thenReturn(carComponentList);
+
+        assertEquals(carComponentList, carComponentService.findAllByType(ComponentTypeEnum.CHASSIS));
+    }
+
+    @Test
+    void delete() {
+        CarComponent carComponent = createFilledComponent(new Car());
+
+        when(carComponentRepository.findById(carComponent.getId())).thenReturn(Optional.of(carComponent));
+        carComponentService.delete(carComponent.getId());
+        verify(carComponentRepository, times(1)).delete(carComponent);
+    }
+
+    @Test
+    void deleteNonExisting() {
+        var exceptionToThrow = new ResourceNotFoundException(CarComponent.class, Long.MAX_VALUE);
+
+        when(carComponentRepository.findById(Long.MAX_VALUE)).thenReturn(Optional.empty());
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carComponentService.delete(Long.MAX_VALUE);
+        });
+    }
+
+    private CarComponent createFilledComponent(Car car) {
+        return new CarComponent(
+                ComponentTypeEnum.CHASSIS,
+                180.0,
+                "Dummy information",
+                car
+        );
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/java/cz/muni/pa165/service/CarServiceTest.java b/core/src/test/java/cz/muni/pa165/service/CarServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..23feaf494a07027c24851ef5827ef495f4dd424b
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/service/CarServiceTest.java
@@ -0,0 +1,347 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.model.Car;
+import cz.muni.pa165.data.model.CarComponent;
+import cz.muni.pa165.data.model.Driver;
+import cz.muni.pa165.data.repository.CarComponentRepository;
+import cz.muni.pa165.data.repository.CarRepository;
+import cz.muni.pa165.data.repository.DriverRepository;
+import cz.muni.pa165.exceptions.BadRequestException;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import static cz.muni.pa165.data.enums.ComponentTypeEnum.CHASSIS;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * @author Michal Badin
+ */
+public class CarServiceTest {
+
+    private CarRepository carRepository;
+    private DriverRepository driverRepository;
+    private CarComponentRepository carComponentRepository;
+    private WebClient.Builder webClientBuilder;
+
+    private CarService carService;
+    private Car car;
+
+    @BeforeEach
+    public void setUp() {
+        carComponentRepository = mock(CarComponentRepository.class);
+        carRepository = mock(CarRepository.class);
+        driverRepository = mock(DriverRepository.class);
+        webClientBuilder = WebClient.builder();
+        carService = new CarService(carRepository, driverRepository, carComponentRepository, webClientBuilder);
+
+        car = new Car();
+        car.setId(1L);
+    }
+
+    @Test
+    void testCreate() {
+        Car createCar = new Car();
+        createCar.setDriver(new Driver());
+        createCar.setComponents(Set.of(new CarComponent()));
+
+        when(carRepository.save(createCar)).thenReturn(createCar);
+
+        assertEquals(carService.create(createCar), createCar);
+        verify(carRepository, times(1)).save(createCar);
+    }
+
+    @Test
+    void testDeleteExistingCar() {
+        car.setComponents(Set.of(new CarComponent()));
+
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+        carService.delete(car.getId());
+
+        verify(carRepository, times(1)).delete(car);
+    }
+
+    @Test
+    void testDeleteNonExistingCar() {
+        Long carId = 1L;
+        var exceptionToThrow = new ResourceNotFoundException(Car.class, carId);
+        Optional<Car> carFromDb = Optional.empty();
+
+        when(carRepository.findById(carId)).thenReturn(carFromDb);
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.delete(carId);
+        });
+    }
+
+    @Test
+    void testUpdateExistingCar() {
+        Car carForUpdate = new Car();
+        CarComponent carComponent = new CarComponent();
+        carComponent.setComponentType(CHASSIS);
+        carComponent.setId(2L);
+        carForUpdate.setComponents(Set.of(carComponent));
+        carForUpdate.setId(car.getId());
+
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+        when(carComponentRepository.findById(carComponent.getId())).thenReturn(Optional.of(carComponent));
+        when(carRepository.save(car)).thenReturn(car);
+
+        Car updatedCar = carService.update(car.getId(), List.of(carComponent.getId()));
+
+        assertEquals(car, updatedCar);
+        assertEquals(1, updatedCar.getComponents().size());
+        assertTrue(updatedCar.getComponents().contains(carComponent));
+        verify(carRepository, times(1)).save(car);
+    }
+
+    @Test
+    void testUpdateNonExistingCar() {
+        Long carId = 1L;
+        var exceptionToThrow = new ResourceNotFoundException(Car.class, carId);
+        List<Long> componentIds = Arrays.asList(2L, 3L);
+        Optional<Car> carFromDb = Optional.empty();
+
+        when(carRepository.findById(carId)).thenReturn(carFromDb);
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.update(carId, componentIds);
+        });
+    }
+
+    @Test
+    void testUpdateExistingCarWithDuplicateComponent() {
+        List<Long> componentIds = Arrays.asList(2L, 2L);
+        var exceptionToThrow = new BadRequestException("Car can only have one component of each type");
+        CarComponent component1 = new CarComponent();
+        component1.setId(2L);
+
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+        when(carComponentRepository.findById(component1.getId())).thenReturn(Optional.of(component1));
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.update(car.getId(), componentIds);
+        });
+    }
+
+    @Test
+    void testFindByIdExistingCar() {
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+
+        assertEquals(car, carService.findById(car.getId()));
+    }
+
+    @Test
+    void testFindByIdNonExistingCar() {
+        Long carId = 1L;
+        var exceptionToThrow = new ResourceNotFoundException(Car.class, carId);
+        Optional<Car> carFromDb = Optional.empty();
+
+        when(carRepository.findById(carId)).thenReturn(carFromDb);
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.findById(carId);
+        });
+    }
+
+    @Test
+    void testSetDriverExistingCar() {
+        Driver driver = new Driver();
+        driver.setId(2L);
+        driver.setName("Driver");
+
+        Car carWithDriver = new Car();
+        carWithDriver.setDriver(driver);
+        carWithDriver.setId(car.getId());
+
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+        when(driverRepository.findById(driver.getId())).thenReturn(Optional.of(driver));
+        when(carRepository.save(car)).thenReturn(car);
+
+        assertEquals(carWithDriver, carService.setDriver(car.getId(), driver.getId()));
+        verify(carRepository, times(1)).save(car);
+    }
+
+    @Test
+    void testSetDriverNonToExistingCar() {
+        Long carId = 1L;
+        var exceptionToThrow = new ResourceNotFoundException(Car.class, carId);
+        Optional<Car> carFromDb = Optional.empty();
+        Driver driver = new Driver();
+        driver.setName("Driver");
+        driver.setId(2L);
+
+        when(carRepository.findById(carId)).thenReturn(carFromDb);
+        when(driverRepository.findById(driver.getId())).thenReturn(Optional.of(driver));
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.setDriver(carId, driver.getId());
+        });
+    }
+
+    @Test
+    void testSetNonExistingDriverToExistingCar() {
+        Long driverId = 2L;
+        var exceptionToThrow = new ResourceNotFoundException(Driver.class, driverId);
+        Optional<Driver> driverFromDb = Optional.empty();
+
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+        when(driverRepository.findById(driverId)).thenReturn(driverFromDb);
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.setDriver(car.getId(), driverId);
+        });
+    }
+
+    @Test
+    void testSetDriverToCarWithDriverOfDifferentCar() {
+        Car car1 = new Car();
+        car1.setId(2L);
+        Driver driver = new Driver();
+        driver.setId(3L);
+        driver.setName("Driver");
+        driver.setCar(car1);
+
+        var exceptionToThrow = new BadRequestException("Driver is already driver of a different car");
+
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+        when(driverRepository.findById(driver.getId())).thenReturn(Optional.of(driver));
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.setDriver(car.getId(), driver.getId());
+        });
+    }
+
+    @Test
+    void testSetDriverToCarWithDriver() {
+        Driver currDriver = new Driver();
+        currDriver.setName("currDriver");
+        currDriver.setId(2L);
+        car.setDriver(currDriver);
+        Driver newDriver = new Driver();
+        newDriver.setName("newDriver");
+        newDriver.setId(3L);
+        var exceptionToThrow = new BadRequestException("Car already has a driver");
+
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+        when(driverRepository.findById(newDriver.getId())).thenReturn(Optional.of(newDriver));
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.setDriver(car.getId(), newDriver.getId());
+        });
+    }
+
+    @Test
+    void testRemoveDriverFromExistingCar() {
+        Driver driver = new Driver();
+        driver.setName("Driver");
+        driver.setId(2L);
+        car.setDriver(driver);
+        driver.setCar(car);
+
+        Car carWithOutDriver = new Car();
+        carWithOutDriver.setId(car.getId());
+
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+        when(driverRepository.findById(driver.getId())).thenReturn(Optional.of(driver));
+        when(driverRepository.save(driver)).thenReturn(driver);
+        when(carRepository.save(car)).thenReturn(car);
+
+        assertEquals(carWithOutDriver, carService.removeDriver(car.getId()));
+        verify(carRepository, times(1)).save(car);
+        verify(driverRepository, times(1)).save(driver);
+    }
+
+    @Test
+    void testRemoveDriverFromNonExistingCar() {
+        Long carId = 1L;
+        Driver driver = new Driver();
+        driver.setId(2L);
+        driver.setName("Driver");
+        var exceptionToThrow = new ResourceNotFoundException(Car.class, carId);
+        Optional<Car> carFromDb = Optional.empty();
+
+        when(carRepository.findById(car.getId())).thenReturn(carFromDb);
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.removeDriver(carId);
+        });
+    }
+
+    @Test
+    void testRemoveNonExistingDriverFromExistingCar() {
+        var exceptionToThrow = new BadRequestException(String.format("Car with id %s does not have a driver", car.getId()));
+
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.removeDriver(car.getId());
+        });
+    }
+
+    @Test
+    void testRemoveDriverFromCarWithoutDriver() {
+        Driver driver = new Driver();
+        driver.setId(2L);
+        driver.setName("driver");
+        car.setDriver(driver);
+        var exceptionToThrow = new ResourceNotFoundException(Driver.class, driver.getId());
+        Optional<Driver> driverFromDb = Optional.empty();
+
+        when(carRepository.findById(car.getId())).thenReturn(Optional.of(car));
+        when(driverRepository.findById(driver.getId())).thenReturn(driverFromDb);
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            carService.removeDriver(car.getId());
+        });
+    }
+
+    @Test
+    void findAll() {
+        var car = new Car();
+        Pageable pageable = PageRequest.of(0, 1);
+        Page<Car> cars = new PageImpl<>(List.of(car), pageable, 1);
+
+        when(carRepository.findAll(pageable)).thenReturn(cars);
+        assertEquals(cars, carService.findAll(0, 1));
+        verify(carRepository, times(1)).findAll(pageable);
+    }
+
+    @Test
+    void findAllEmpty() {
+        Pageable pageable = PageRequest.of(0, 1);
+        Page<Car> cars = new PageImpl<>(List.of(), pageable, 0);
+
+        when(carRepository.findAll(pageable)).thenReturn(cars);
+        assertEquals(cars, carService.findAll(0, 1));
+        verify(carRepository, times(1)).findAll(pageable);
+    }
+
+    @Test
+    void findAllLast() {
+        var car = new Car();
+        Pageable pageable = PageRequest.of(1, 2);
+        Page<Car> cars = new PageImpl<>(List.of(car), pageable, 1);
+
+        when(carRepository.findAll(pageable)).thenReturn(cars);
+
+        var result = carService.findAll(1, 2);
+        assertEquals(cars, result);
+        assertTrue(result.isLast());
+        verify(carRepository, times(1)).findAll(pageable);
+    }
+}
diff --git a/core/src/test/java/cz/muni/pa165/service/DepartmentServiceTest.java b/core/src/test/java/cz/muni/pa165/service/DepartmentServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..72dcb8c99ffe195ea853d18c3f8b5c03367d0a26
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/service/DepartmentServiceTest.java
@@ -0,0 +1,88 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.model.Department;
+import cz.muni.pa165.data.model.Engineer;
+import cz.muni.pa165.data.repository.DepartmentRepository;
+import cz.muni.pa165.data.repository.EngineerRepository;
+import cz.muni.pa165.exceptions.BadRequestException;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.HashSet;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.when;
+
+class DepartmentServiceTest {
+
+    DepartmentRepository departmentRepository = Mockito.mock(DepartmentRepository.class);
+    EngineerRepository engineerRepository = Mockito.mock(EngineerRepository.class);
+    DepartmentService service = new DepartmentService(departmentRepository, engineerRepository);
+    Department emptyDepartment = new Department("aero", new HashSet<>());
+
+
+    @Test
+    void create() {
+        when(departmentRepository.save(emptyDepartment)).thenReturn(emptyDepartment);
+
+        Department created = service.create(emptyDepartment);
+
+        assertEquals(emptyDepartment, created);
+    }
+
+    @Test
+    void addExistingEngineer() {
+        Engineer engineer = new Engineer();
+        Department expected = new Department("test", new HashSet<>());
+        expected.addEngineer(engineer);
+
+        when(engineerRepository.findById(engineer.getId())).thenReturn(java.util.Optional.of(engineer));
+        when(departmentRepository.findById(emptyDepartment.getId())).thenReturn(java.util.Optional.of(emptyDepartment));
+        when(departmentRepository.save(emptyDepartment)).thenReturn(expected);
+
+        assertEquals(expected, service.addEngineer(emptyDepartment.getId(), engineer.getId()));
+    }
+
+    @Test
+    void addEngineerAgain() {
+        Engineer engineer = new Engineer();
+        emptyDepartment.addEngineer(engineer);
+
+        when(engineerRepository.findById(engineer.getId())).thenReturn(java.util.Optional.of(engineer));
+        when(departmentRepository.findById(emptyDepartment.getId())).thenReturn(java.util.Optional.of(emptyDepartment));
+
+        assertThrows(BadRequestException.class, () -> service.addEngineer(emptyDepartment.getId(), engineer.getId()));
+    }
+
+    @Test
+    void addNonExistingEngineer() {
+        when(engineerRepository.findById(1L)).thenReturn(Optional.empty());
+        when(departmentRepository.findById(emptyDepartment.getId())).thenReturn(java.util.Optional.of(emptyDepartment));
+
+        assertThrows(ResourceNotFoundException.class, () -> service.addEngineer(emptyDepartment.getId(), 1L));
+    }
+
+    @Test
+    void updateSpecialization() {
+        Department expected = new Department("Changed", new HashSet<>());
+
+        when(departmentRepository.findById(emptyDepartment.getId())).thenReturn(java.util.Optional.of(emptyDepartment));
+        when(departmentRepository.save(emptyDepartment)).thenReturn(expected);
+
+        assertEquals(expected, service.updateSpecialization(emptyDepartment.getId(), expected));
+    }
+
+    @Test
+    void updateSpecializationEmpty() {
+        Department expected = new Department("", new HashSet<>());
+
+        when(departmentRepository.findById(emptyDepartment.getId())).thenReturn(java.util.Optional.of(emptyDepartment));
+        when(departmentRepository.save(emptyDepartment)).thenReturn(expected);
+
+        assertEquals(expected, service.updateSpecialization(emptyDepartment.getId(), expected));
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/test/java/cz/muni/pa165/service/DriverServiceTest.java b/core/src/test/java/cz/muni/pa165/service/DriverServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..842f2678ada8eec16bb2ff1eee257e473ad7d532
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/service/DriverServiceTest.java
@@ -0,0 +1,138 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.enums.CharacteristicsEnum;
+import cz.muni.pa165.data.model.Car;
+import cz.muni.pa165.data.model.CarComponent;
+import cz.muni.pa165.data.model.Driver;
+import cz.muni.pa165.data.repository.DriverRepository;
+import cz.muni.pa165.exceptions.BadRequestException;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.*;
+
+class DriverServiceTest {
+    private DriverService driverService;
+    private DriverRepository driverRepository;
+
+    @BeforeEach
+    void setUp() {
+        driverRepository = mock(DriverRepository.class);
+        driverService = new DriverService(driverRepository);
+    }
+
+    @Test
+    void create() {
+        var driver = createDriver();
+
+        when(driverRepository.save(driver)).thenReturn(driver);
+
+        assertEquals(driver, driverService.create(driver));
+        verify(driverRepository, times(1)).save(driver);
+    }
+
+    @Test
+    void updateExistingDriver() {
+        var driver = createDriver();
+        var driverForUpdate = new Driver("jan", "novak", 180,
+                LocalDate.of(1990, 1, 1), "czech", null,
+                new HashSet<>(List.of(CharacteristicsEnum.AGGRESSIVENESS)));
+
+        when(driverRepository.findById(driver.getId())).thenReturn(Optional.of(driver));
+        when(driverRepository.save(driver)).thenReturn(driver);
+
+        assertEquals(driverForUpdate, driverService.update(driver.getId(), driverForUpdate));
+        verify(driverRepository, times(1)).save(driver);
+    }
+
+    @Test
+    void updateNonExistingDriver() {
+        var exceptionToThrow = new ResourceNotFoundException(CarComponent.class, Long.MAX_VALUE);
+
+        when(driverRepository.findById(Long.MAX_VALUE)).thenReturn(Optional.empty());
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            driverService.update(Long.MAX_VALUE, null);
+        });
+    }
+
+    @Test
+    void findExistingComponentById() {
+        var driver = createDriver();
+
+        when(driverRepository.findById(driver.getId())).thenReturn(Optional.of(driver));
+
+        assertEquals(driver, driverService.findById(driver.getId()));
+    }
+
+    @Test
+    void findNonExistingDriverById() {
+        var exceptionToThrow = new ResourceNotFoundException(CarComponent.class, Long.MAX_VALUE);
+
+        when(driverRepository.findById(Long.MAX_VALUE)).thenReturn(Optional.empty());
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            driverService.findById(Long.MAX_VALUE);
+        });
+    }
+
+    @Test
+    void findAllTestDrivers() {
+        var driver = createDriver();
+        var driver2 = new Driver("jana", "novakova", 175,
+                LocalDate.of(1990, 1, 1), "czech", null, null);
+
+        var testDrivers = List.of(driver, driver2);
+
+        when(driverRepository.findAllTestDrivers()).thenReturn(testDrivers);
+
+        assertEquals(testDrivers, driverService.findAllTestDrivers());
+    }
+
+    @Test
+    void delete() {
+        var driver = createDriver();
+
+        when(driverRepository.findById(driver.getId())).thenReturn(Optional.of(driver));
+        driverService.delete(driver.getId());
+        verify(driverRepository, times(1)).delete(driver);
+    }
+
+    @Test
+    void deleteNonExisting() {
+        var exceptionToThrow = new ResourceNotFoundException(CarComponent.class, Long.MAX_VALUE);
+
+        when(driverRepository.findById(Long.MAX_VALUE)).thenReturn(Optional.empty());
+
+        assertThrows(exceptionToThrow.getClass(), () -> {
+            driverService.delete(Long.MAX_VALUE);
+        });
+    }
+
+    @Test
+    void deleteDriverWithCar() {
+        var driver = createDriver();
+        driver.setCar(new Car());
+
+        when(driverRepository.findById(driver.getId())).thenReturn(Optional.of(driver));
+        assertThrows(BadRequestException.class, () -> {
+            driverService.delete(driver.getId());
+        });
+    }
+
+    private Driver createDriver() {
+        var driver = new Driver("jan", "novak", 175,
+                LocalDate.of(1990, 1, 1), "czech", null, null);
+
+        driver.setId(1L);
+        return driver;
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/java/cz/muni/pa165/service/EngineerServiceTest.java b/core/src/test/java/cz/muni/pa165/service/EngineerServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c51b597ab5740efaba4d106f2da9773e9aea7349
--- /dev/null
+++ b/core/src/test/java/cz/muni/pa165/service/EngineerServiceTest.java
@@ -0,0 +1,44 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.data.model.Engineer;
+import cz.muni.pa165.data.repository.EngineerRepository;
+import cz.muni.pa165.exceptions.ResourceNotFoundException;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.when;
+
+class EngineerServiceTest {
+
+    EngineerRepository engineerRepository = Mockito.mock(EngineerRepository.class);
+    EngineerService service = new EngineerService(engineerRepository);
+    Engineer engineer = new Engineer();
+
+    @Test
+    void create() {
+        when(engineerRepository.save(engineer)).thenReturn(engineer);
+
+        Engineer created = service.create(engineer);
+
+        assertEquals(engineer, created);
+    }
+
+    @Test
+    void findExistingEngineerById() {
+        when(engineerRepository.findById(engineer.getId())).thenReturn(Optional.of(engineer));
+        assertEquals(engineer, service.findById(engineer.getId()));
+    }
+
+    @Test
+    void findNonExistingEngineerById() {
+        when(engineerRepository.findById(1L)).thenReturn(Optional.empty());
+
+        assertThrows(ResourceNotFoundException.class, () -> service.findById(1L));
+    }
+}
+
+
diff --git a/core/src/test/resources/application-test.properties b/core/src/test/resources/application-test.properties
new file mode 100644
index 0000000000000000000000000000000000000000..ccb1db0eb36353a3ad1465eccb608c2261081a2b
--- /dev/null
+++ b/core/src/test/resources/application-test.properties
@@ -0,0 +1,14 @@
+# For description of each field check: https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# INIT=CREATE SCHEMA IF NOT EXISTS PA165;SET SCHEMA PA165
+spring.datasource.url=jdbc:h2:mem:testdb;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
+spring.datasource.username=formulky-test
+spring.datasource.password=
+spring.datasource.driverClassName=org.h2.Driver
+spring.jpa.hibernate.ddl-auto=create
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
+spring.jpa.properties.hibernate.generate_statistics=true
+spring.jpa.properties.hibernate.format_sql=true
+spring.jpa.properties.hibernate.show_sql=false
+spring.h2.console.enabled=true
+spring.cache.type=NONE
+appconfig.enablecache=false
\ No newline at end of file
diff --git a/core/src/test/resources/logback.xml b/core/src/test/resources/logback.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6690d6a5e55b0d38783a9ddef0471132ce6ddbe9
--- /dev/null
+++ b/core/src/test/resources/logback.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%d %5p %40.40c:%4L - %m%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="info">
+        <appender-ref ref="console"/>
+    </root>
+    <!-- TODO remove solution-->
+    <logger name="org.hibernate.SQL" level="DEBUG"/>
+
+</configuration>
\ No newline at end of file
diff --git a/dashboards/dashboard.yml b/dashboards/dashboard.yml
new file mode 100644
index 0000000000000000000000000000000000000000..528c704cd0cf904907faa81fc0a30322d6c560ed
--- /dev/null
+++ b/dashboards/dashboard.yml
@@ -0,0 +1,7 @@
+apiVersion: 1
+
+providers:
+  - name: 'Default'
+    folder: 'Services'
+    options:
+      path: /etc/grafana/provisioning/dashboards
\ No newline at end of file
diff --git a/dashboards/grafana.json b/dashboards/grafana.json
new file mode 100644
index 0000000000000000000000000000000000000000..9f50a3b93f72166a68f5509a5ea5e491c89c2b54
--- /dev/null
+++ b/dashboards/grafana.json
@@ -0,0 +1,319 @@
+{
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": {
+          "type": "grafana",
+          "uid": "-- Grafana --"
+        },
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "target": {
+          "limit": 100,
+          "matchAny": false,
+          "tags": [],
+          "type": "dashboard"
+        },
+        "type": "dashboard"
+      }
+    ]
+  },
+  "editable": true,
+  "fiscalYearStartMonth": 0,
+  "graphTooltip": 0,
+  "id": 1,
+  "links": [],
+  "liveNow": false,
+  "panels": [
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "BhexAJs4z"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 0,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "__systemRef": "hideSeriesFrom",
+            "matcher": {
+              "id": "byNames",
+              "options": {
+                "mode": "exclude",
+                "names": [
+                  "{__name__=\"disk_free_bytes\", instance=\"host.docker.internal:8080\", job=\"formula-core\", path=\"/.\"}"
+                ],
+                "prefix": "All except:",
+                "readOnly": true
+              }
+            },
+            "properties": [
+              {
+                "id": "custom.hideFrom",
+                "value": {
+                  "legend": false,
+                  "tooltip": false,
+                  "viz": true
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 0,
+        "y": 0
+      },
+      "id": 2,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "p5SIZnEVk"
+          },
+          "editorMode": "builder",
+          "expr": "disk_free_bytes",
+          "legendFormat": "__auto",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Free Bytes on Disk",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "BhexAJs4z"
+      },
+      "description": "",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 0,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "smooth",
+            "lineWidth": 3,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 0
+      },
+      "id": 4,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "p5SIZnEVk"
+          },
+          "editorMode": "code",
+          "expr": "go_memstats_alloc_bytes",
+          "legendFormat": "__auto",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Allocated Bytes",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {},
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 9
+      },
+      "id": 8,
+      "options": {
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "showThresholdLabels": false,
+        "showThresholdMarkers": true
+      },
+      "pluginVersion": "9.1.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "p5SIZnEVk"
+          },
+          "editorMode": "code",
+          "expr": "http_server_requests_seconds_count",
+          "legendFormat": "__auto",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "HTTP Server Requests counter",
+      "type": "gauge"
+    }
+  ],
+  "refresh": "5s",
+  "schemaVersion": 37,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": []
+  },
+  "time": {
+    "from": "now-6h",
+    "to": "now"
+  },
+  "timepicker": {},
+  "timezone": "",
+  "title": "Formula Team Management",
+  "uid": "YDzAhnEVz",
+  "version": 3,
+  "weekStart": ""
+}
\ No newline at end of file
diff --git a/dashboards/justai-system-monitor_rev2.json b/dashboards/justai-system-monitor_rev2.json
new file mode 100644
index 0000000000000000000000000000000000000000..b7fe8317e4cdbeaa650c1d99449bc318b127b98a
--- /dev/null
+++ b/dashboards/justai-system-monitor_rev2.json
@@ -0,0 +1,3688 @@
+{
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": {
+          "type": "datasource",
+          "uid": "grafana"
+        },
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "target": {
+          "limit": 100,
+          "matchAny": false,
+          "tags": [],
+          "type": "dashboard"
+        },
+        "type": "dashboard"
+      }
+    ]
+  },
+  "description": "Dashboard for Spring Boot2.1 Statistics(based on Spring Boot2 Statistic by micrometer-prometheus).",
+  "editable": false,
+  "fiscalYearStartMonth": 0,
+  "gnetId": 11378,
+  "graphTooltip": 0,
+  "id": 2,
+  "links": [],
+  "liveNow": false,
+  "panels": [
+    {
+      "collapsed": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 0
+      },
+      "id": 54,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "Basic Statistics",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "decimals": 1,
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "s"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 5,
+        "x": 0,
+        "y": 1
+      },
+      "id": 52,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "process_uptime_seconds{application=\"$application\", instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "",
+          "metric": "",
+          "refId": "A",
+          "step": 14400
+        }
+      ],
+      "title": "Uptime",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "decimals": 1,
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "max": 100,
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "rgba(50, 172, 45, 0.97)",
+                "value": null
+              },
+              {
+                "color": "rgba(237, 129, 40, 0.89)",
+                "value": 70
+              },
+              {
+                "color": "rgba(245, 54, 54, 0.9)",
+                "value": 90
+              }
+            ]
+          },
+          "unit": "percent"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 6,
+        "w": 5,
+        "x": 5,
+        "y": 1
+      },
+      "id": 58,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "showThresholdLabels": false,
+        "showThresholdMarkers": true
+      },
+      "pluginVersion": "9.1.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=\"$instance\", area=\"heap\"})",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "",
+          "refId": "A",
+          "step": 14400
+        }
+      ],
+      "title": "Heap Used",
+      "type": "gauge"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "decimals": 1,
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            },
+            {
+              "options": {
+                "from": -1e+32,
+                "result": {
+                  "text": "N/A"
+                },
+                "to": 0
+              },
+              "type": "range"
+            }
+          ],
+          "max": 100,
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "rgba(50, 172, 45, 0.97)",
+                "value": null
+              },
+              {
+                "color": "rgba(237, 129, 40, 0.89)",
+                "value": 70
+              },
+              {
+                "color": "rgba(245, 54, 54, 0.9)",
+                "value": 90
+              }
+            ]
+          },
+          "unit": "percent"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 6,
+        "w": 5,
+        "x": 10,
+        "y": 1
+      },
+      "id": 60,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "showThresholdLabels": false,
+        "showThresholdMarkers": true
+      },
+      "pluginVersion": "9.1.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=\"$instance\", area=\"nonheap\"})",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "",
+          "refId": "A",
+          "step": 14400
+        }
+      ],
+      "title": "Non-Heap Used",
+      "type": "gauge"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "links": []
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 6,
+        "w": 9,
+        "x": 15,
+        "y": 1
+      },
+      "hiddenSeries": false,
+      "id": 66,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "alertThreshold": true
+      },
+      "percentage": false,
+      "pluginVersion": "9.1.7",
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "process_files_open_files{application=\"$application\", instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Open Files",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "process_files_max_files{application=\"$application\", instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Max Files",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Process Open Files",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "locale",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "dateTimeAsIso"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 5,
+        "x": 0,
+        "y": 4
+      },
+      "id": 56,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "process_start_time_seconds{application=\"$application\", instance=\"$instance\"}*1000",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "",
+          "metric": "",
+          "refId": "A",
+          "step": 14400
+        }
+      ],
+      "title": "Start time",
+      "type": "stat"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "links": []
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 0,
+        "y": 7
+      },
+      "hiddenSeries": false,
+      "id": 95,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "alertThreshold": true
+      },
+      "percentage": false,
+      "pluginVersion": "9.1.7",
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "system_cpu_usage{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "System CPU Usage",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "process_cpu_usage{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Process CPU Usage",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "CPU Usage",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "links": []
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 12,
+        "y": 7
+      },
+      "hiddenSeries": false,
+      "id": 96,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "alertThreshold": true
+      },
+      "percentage": false,
+      "pluginVersion": "9.1.7",
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "system_load_average_1m{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Load Average [1m]",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "system_cpu_count{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "CPU Core Size",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Load Average",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 14
+      },
+      "id": 48,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "JVM Statistics - Heaps",
+      "type": "row"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "links": []
+        },
+        "overrides": []
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 8,
+        "w": 8,
+        "x": 0,
+        "y": 15
+      },
+      "hiddenSeries": false,
+      "id": 85,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "alertThreshold": true
+      },
+      "percentage": false,
+      "pluginVersion": "9.1.7",
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "repeat": "memory_pool_heap",
+      "repeatDirection": "h",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_memory_used_bytes{instance=\"$instance\", application=\"$application\", id=\"$memory_pool_heap\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Used",
+          "refId": "C"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_memory_committed_bytes{instance=\"$instance\", application=\"$application\", id=\"$memory_pool_heap\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Commited",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_memory_max_bytes{instance=\"$instance\", application=\"$application\", id=\"$memory_pool_heap\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Max",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "$memory_pool_heap (heap)",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "bytes",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 8,
+        "w": 6,
+        "x": 0,
+        "y": 23
+      },
+      "hiddenSeries": false,
+      "id": 88,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "repeat": "memory_pool_nonheap",
+      "repeatDirection": "h",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_memory_used_bytes{instance=\"$instance\", application=\"$application\", id=\"$memory_pool_nonheap\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Used",
+          "refId": "C"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_memory_committed_bytes{instance=\"$instance\", application=\"$application\", id=\"$memory_pool_nonheap\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Commited",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_memory_max_bytes{instance=\"$instance\", application=\"$application\", id=\"$memory_pool_nonheap\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Max",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "$memory_pool_nonheap (non-heap)",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "bytes",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 39
+      },
+      "id": 117,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "JVM Statistics Threads/Buffers",
+      "type": "row"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 0,
+        "y": 40
+      },
+      "hiddenSeries": false,
+      "id": 68,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_threads_daemon_threads{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Daemon",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_threads_live_threads{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Live",
+          "refId": "B"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_threads_peak_threads{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Peak",
+          "refId": "C"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Threads",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 12,
+        "y": 40
+      },
+      "hiddenSeries": false,
+      "id": 78,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "irate(jvm_gc_memory_allocated_bytes_total{instance=\"$instance\", application=\"$application\"}[5m])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "allocated",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "irate(jvm_gc_memory_promoted_bytes_total{instance=\"$instance\", application=\"$application\"}[5m])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "promoted",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Memory Allocate/Promote",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "bytes",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "decimals": 0,
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 0,
+        "y": 47
+      },
+      "hiddenSeries": false,
+      "id": 50,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_classes_loaded_classes{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Classes Loaded",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Classes Loaded",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "decimals": 0,
+          "format": "locale",
+          "label": "",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 12,
+        "y": 47
+      },
+      "hiddenSeries": false,
+      "id": 80,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "irate(jvm_classes_unloaded_classes_total{instance=\"$instance\", application=\"$application\"}[5m])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Classes Unloaded",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Classes Unloaded",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 0,
+        "y": 54
+      },
+      "hiddenSeries": false,
+      "id": 82,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_buffer_memory_used_bytes{instance=\"$instance\", application=\"$application\", id=\"direct\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Used Bytes",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_buffer_total_capacity_bytes{instance=\"$instance\", application=\"$application\", id=\"direct\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Capacity Bytes",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Direct Buffers",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 12,
+        "y": 54
+      },
+      "hiddenSeries": false,
+      "id": 83,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_buffer_memory_used_bytes{instance=\"$instance\", application=\"$application\", id=\"mapped\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Used Bytes",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jvm_buffer_total_capacity_bytes{instance=\"$instance\", application=\"$application\", id=\"mapped\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Capacity Bytes",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Mapped Buffers",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 61
+      },
+      "id": 72,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "JVM Statistics - GC",
+      "type": "row"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 62
+      },
+      "hiddenSeries": false,
+      "id": 74,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": false,
+        "hideEmpty": true,
+        "hideZero": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "irate(jvm_gc_pause_seconds_count{instance=\"$instance\", application=\"$application\"}[5m])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{action}} [{{cause}}]",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "GC Count",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "locale",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 62
+      },
+      "hiddenSeries": false,
+      "id": 76,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": false,
+        "hideEmpty": true,
+        "hideZero": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "irate(jvm_gc_pause_seconds_sum{instance=\"$instance\", application=\"$application\"}[5m])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{action}} [{{cause}}]",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "GC Stop the World Duration",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "s",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 70
+      },
+      "id": 34,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "HikariCP Statistics",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 4,
+        "w": 4,
+        "x": 0,
+        "y": 71
+      },
+      "id": 44,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "hikaricp_connections{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "",
+          "refId": "A"
+        }
+      ],
+      "title": "Connections Size",
+      "type": "stat"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 8,
+        "w": 20,
+        "x": 4,
+        "y": 71
+      },
+      "hiddenSeries": false,
+      "id": 36,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "hideEmpty": true,
+        "hideZero": false,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": true,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "hikaricp_connections_active{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Active",
+          "refId": "B"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "hikaricp_connections_idle{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Idle",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "hikaricp_connections_pending{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Pending",
+          "refId": "C"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Connections",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 4,
+        "w": 4,
+        "x": 0,
+        "y": 75
+      },
+      "id": 46,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "hikaricp_connections_timeout_total{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "",
+          "refId": "A"
+        }
+      ],
+      "title": "Connection Timeout Count",
+      "type": "stat"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 6,
+        "w": 8,
+        "x": 0,
+        "y": 79
+      },
+      "hiddenSeries": false,
+      "id": 38,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "hikaricp_connections_creation_seconds_sum{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"} / hikaricp_connections_creation_seconds_count{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Creation Time",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Connection Creation Time",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "s",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 6,
+        "w": 8,
+        "x": 8,
+        "y": 79
+      },
+      "hiddenSeries": false,
+      "id": 42,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "hikaricp_connections_usage_seconds_sum{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"} / hikaricp_connections_usage_seconds_count{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Usage Time",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Connection Usage Time",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "s",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 6,
+        "w": 8,
+        "x": 16,
+        "y": 79
+      },
+      "hiddenSeries": false,
+      "id": 40,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "hikaricp_connections_acquire_seconds_sum{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"} / hikaricp_connections_acquire_seconds_count{instance=\"$instance\", application=\"$application\", pool=\"$hikaricp\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Acquire Time",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Connection Acquire Time",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "s",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 85
+      },
+      "id": 22,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "Jetty Statistics",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "locale"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 4,
+        "w": 4,
+        "x": 0,
+        "y": 86
+      },
+      "id": 101,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jetty_threads_config_min{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Min threads",
+          "refId": "A"
+        }
+      ],
+      "title": "Thread Config Min",
+      "type": "stat"
+    },
+    {
+      "aliasColors": {
+        "HTTP": "#890f02",
+        "HTTP - 5xx": "#bf1b00"
+      },
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 8,
+        "w": 20,
+        "x": 4,
+        "y": 86
+      },
+      "hiddenSeries": false,
+      "id": 111,
+      "legend": {
+        "avg": false,
+        "current": true,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status=~\"5..\"}[1m]))",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "5xx - Server Errors",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status=~\"4..\"}[1m]))",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "4xx - Client Errors",
+          "refId": "B"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status=~\"2..\"}[1m]))",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "2xx - Success",
+          "refId": "C"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status=~\"3..\"}[1m]))",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "3xx - Redirections",
+          "refId": "D"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "HTTP Codes",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "ops",
+          "logBase": 1,
+          "min": "0",
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "locale"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 4,
+        "w": 4,
+        "x": 0,
+        "y": 90
+      },
+      "id": 32,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jetty_threads_config_max{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Max threads",
+          "refId": "A"
+        }
+      ],
+      "title": "Thread Config Max",
+      "type": "stat"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 0,
+        "y": 94
+      },
+      "hiddenSeries": false,
+      "id": 107,
+      "legend": {
+        "avg": false,
+        "current": false,
+        "max": false,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pluginVersion": "6.5.1",
+      "pointradius": 2,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\"}[1m]))",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 1,
+          "legendFormat": "Requests per second",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Requests/second",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 12,
+        "y": 94
+      },
+      "hiddenSeries": false,
+      "id": 108,
+      "legend": {
+        "avg": false,
+        "current": true,
+        "max": true,
+        "min": false,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 2,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "sum(rate(http_server_requests_seconds_sum{application=\"$application\", instance=\"$instance\", status!~\"5..\"}[1m]))/sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status!~\"5..\"}[1m]))",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 1,
+          "legendFormat": "Average",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "max(http_server_requests_seconds_max{application=\"$application\", instance=\"$instance\", status!~\"5..\"})",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 1,
+          "legendFormat": "Maximum",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Requests duration",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 0,
+        "y": 101
+      },
+      "hiddenSeries": false,
+      "id": 109,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "topk(10, sum by(uri, method) (rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\"}[1m])))",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{uri}}",
+          "refId": "D"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "TOP 10",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 9,
+        "w": 12,
+        "x": 12,
+        "y": 101
+      },
+      "hiddenSeries": false,
+      "id": 30,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jetty_threads_current{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Current thread",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jetty_threads_busy{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Busy thread",
+          "refId": "B"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jetty_threads_idle{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Idle threads",
+          "refId": "C"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "jetty_threads_jobs{instance=\"$instance\", application=\"$application\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "Jobs  threads",
+          "refId": "D"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "Threads",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 110
+      },
+      "id": 8,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "Logback Statistics",
+      "type": "row"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 0,
+        "y": 111
+      },
+      "hiddenSeries": false,
+      "id": 6,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "irate(logback_events_total{instance=\"$instance\", application=\"$application\", level=\"info\"}[5m])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "info",
+          "rawSql": "SELECT\n  $__time(time_column),\n  value1\nFROM\n  metric_table\nWHERE\n  $__timeFilter(time_column)\n",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "INFO logs",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "none",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 12,
+        "x": 12,
+        "y": 111
+      },
+      "hiddenSeries": false,
+      "id": 10,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "irate(logback_events_total{instance=\"$instance\", application=\"$application\", level=\"error\"}[5m])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "error",
+          "rawSql": "SELECT\n  $__time(time_column),\n  value1\nFROM\n  metric_table\nWHERE\n  $__timeFilter(time_column)\n",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "ERROR logs",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "none",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 0,
+        "y": 118
+      },
+      "hiddenSeries": false,
+      "id": 14,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "irate(logback_events_total{instance=\"$instance\", application=\"$application\", level=\"warn\"}[5m])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "warn",
+          "rawSql": "SELECT\n  $__time(time_column),\n  value1\nFROM\n  metric_table\nWHERE\n  $__timeFilter(time_column)\n",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "WARN logs",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "none",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 8,
+        "y": 118
+      },
+      "hiddenSeries": false,
+      "id": 16,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "irate(logback_events_total{instance=\"$instance\", application=\"$application\", level=\"debug\"}[5m])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "debug",
+          "rawSql": "SELECT\n  $__time(time_column),\n  value1\nFROM\n  metric_table\nWHERE\n  $__timeFilter(time_column)\n",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "DEBUG logs",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "none",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fill": 1,
+      "fillGradient": 0,
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 16,
+        "y": 118
+      },
+      "hiddenSeries": false,
+      "id": 20,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "total": true,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "nullPointMode": "null",
+      "options": {
+        "dataLinks": []
+      },
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "alias": "",
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "expr": "irate(logback_events_total{instance=\"$instance\", application=\"$application\", level=\"trace\"}[5m])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "trace",
+          "rawSql": "SELECT\n  $__time(time_column),\n  value1\nFROM\n  metric_table\nWHERE\n  $__timeFilter(time_column)\n",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeRegions": [],
+      "title": "TRACE logs",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "mode": "time",
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "none",
+          "logBase": 1,
+          "show": true
+        },
+        {
+          "format": "short",
+          "logBase": 1,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false
+      }
+    }
+  ],
+  "refresh": "5s",
+  "schemaVersion": 37,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": [
+      {
+        "current": {
+          "isNone": true,
+          "selected": false,
+          "text": "None",
+          "value": ""
+        },
+        "datasource": {
+          "type": "prometheus",
+          "uid": "PBFA97CFB590B2093"
+        },
+        "definition": "label_values(application)",
+        "hide": 0,
+        "includeAll": false,
+        "label": "Application",
+        "multi": false,
+        "name": "application",
+        "options": [],
+        "query": {
+          "query": "label_values(application)",
+          "refId": "Prometheus-application-Variable-Query"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "current": {
+          "selected": false,
+          "text": "host.docker.internal:8081",
+          "value": "host.docker.internal:8081"
+        },
+        "datasource": {
+          "type": "prometheus",
+          "uid": "PBFA97CFB590B2093"
+        },
+        "definition": "label_values(jvm_classes_loaded_classes{application=\"$application\"}, instance)",
+        "hide": 0,
+        "includeAll": false,
+        "label": "Instance",
+        "multi": false,
+        "name": "instance",
+        "options": [],
+        "query": {
+          "query": "label_values(jvm_classes_loaded_classes{application=\"$application\"}, instance)",
+          "refId": "Prometheus-instance-Variable-Query"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "current": {
+          "selected": false,
+          "text": "HikariPool-1",
+          "value": "HikariPool-1"
+        },
+        "datasource": {
+          "type": "prometheus",
+          "uid": "PBFA97CFB590B2093"
+        },
+        "definition": "",
+        "hide": 0,
+        "includeAll": false,
+        "label": "HikariCP-Pool",
+        "multi": false,
+        "name": "hikaricp",
+        "options": [],
+        "query": {
+          "query": "label_values(hikaricp_connections{instance=\"$instance\", application=\"$application\"}, pool)",
+          "refId": "Prometheus-hikaricp-Variable-Query"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "current": {
+          "selected": false,
+          "text": "All",
+          "value": "$__all"
+        },
+        "datasource": {
+          "type": "prometheus",
+          "uid": "PBFA97CFB590B2093"
+        },
+        "definition": "",
+        "hide": 0,
+        "includeAll": true,
+        "label": "Memory Pool (heap)",
+        "multi": false,
+        "name": "memory_pool_heap",
+        "options": [],
+        "query": {
+          "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"},id)",
+          "refId": "Prometheus-memory_pool_heap-Variable-Query"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "current": {
+          "selected": false,
+          "text": "All",
+          "value": "$__all"
+        },
+        "datasource": {
+          "type": "prometheus",
+          "uid": "PBFA97CFB590B2093"
+        },
+        "definition": "",
+        "hide": 0,
+        "includeAll": true,
+        "label": "Memory Pool (nonheap)",
+        "multi": false,
+        "name": "memory_pool_nonheap",
+        "options": [],
+        "query": {
+          "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"},id)",
+          "refId": "Prometheus-memory_pool_nonheap-Variable-Query"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      }
+    ]
+  },
+  "time": {
+    "from": "now-1h",
+    "to": "now"
+  },
+  "timepicker": {
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "time_options": [
+      "5m",
+      "15m",
+      "1h",
+      "6h",
+      "12h",
+      "24h",
+      "2d",
+      "7d",
+      "30d"
+    ]
+  },
+  "timezone": "",
+  "title": "Spring Boot 2.1 System Monitor",
+  "uid": "spring_boot_21",
+  "version": 1,
+  "weekStart": ""
+}
\ No newline at end of file
diff --git a/datasource.yml b/datasource.yml
new file mode 100644
index 0000000000000000000000000000000000000000..782d766a00b6b4ff50db06d0708a22496fbbb16e
--- /dev/null
+++ b/datasource.yml
@@ -0,0 +1,7 @@
+apiVersion: 1
+
+datasources:
+  - name: Prometheus
+    type: prometheus
+    access: proxy
+    url: http://prometheus:9090
\ No newline at end of file
diff --git a/diagrams/class_diagram.png b/diagrams/class_diagram.png
new file mode 100644
index 0000000000000000000000000000000000000000..afbe55e89eea20ec73c1564a90747aee4063d770
Binary files /dev/null and b/diagrams/class_diagram.png differ
diff --git a/diagrams/classdia b/diagrams/classdia
new file mode 100644
index 0000000000000000000000000000000000000000..d5a024f858e6b6c65074f9ecc0e04ef97791d587
--- /dev/null
+++ b/diagrams/classdia
@@ -0,0 +1,326 @@
+<mxfile host="app.diagrams.net" modified="2023-04-14T16:26:07.746Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36" etag="6uAcyEdPu8KVCyzeOQgq" version="21.1.1" type="device">
+  <diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
+    <mxGraphModel dx="1434" dy="764" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" background="none" math="0" shadow="0">
+      <root>
+        <mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
+        <mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
+        <mxCell id="UvfdVoFo5HVwESIDpEAG-1" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
+          <mxGeometry x="304" y="569" width="517" height="632" as="geometry" />
+        </mxCell>
+        <mxCell id="zkfFHV4jXpPFQw0GAbJ--0" value="Car" style="swimlane;fontStyle=2;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="136" y="76" width="170" height="112" as="geometry">
+            <mxRectangle x="230" y="140" width="160" height="26" as="alternateBounds" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="zkfFHV4jXpPFQw0GAbJ--1" value="ID" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
+          <mxGeometry y="26" width="170" height="26" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-72" value="Components []" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
+          <mxGeometry y="52" width="170" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-77" value="ListComponentsWithDesc()" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
+          <mxGeometry y="82" width="170" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="zkfFHV4jXpPFQw0GAbJ--17" value="Driver" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="396" y="39" width="160" height="146" as="geometry">
+            <mxRectangle x="550" y="140" width="160" height="26" as="alternateBounds" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-3" value="Name" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
+          <mxGeometry y="26" width="160" height="26" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-2" value="Characteristics" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
+          <mxGeometry y="52" width="160" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-53" value="Nationality" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
+          <mxGeometry y="82" width="160" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-60" value="ShowCharacteristics()" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
+          <mxGeometry y="112" width="160" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-4" value="Component" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="106" y="276" width="210" height="120" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-3" value="ID" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="HwPxVh02NSwoOAiZ7XNH-4" vertex="1">
+          <mxGeometry y="30" width="210" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-4" value="Weight" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="HwPxVh02NSwoOAiZ7XNH-4" vertex="1">
+          <mxGeometry y="60" width="210" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-63" value="Description" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="HwPxVh02NSwoOAiZ7XNH-4" vertex="1">
+          <mxGeometry y="90" width="210" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-9" value="composed of" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="188" y="218" width="87" height="26" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-10" value="Engine" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontColor=#B5739D;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="24" y="446" width="82" height="56" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-14" value="Chasis" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontColor=#B5739D;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="126" y="446" width="92" height="57" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-16" value="Front wing" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontColor=#B5739D;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="232" y="446" width="98" height="60" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-21" value="Department" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="416" y="286" width="170" height="116" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-5" value="ID" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="HwPxVh02NSwoOAiZ7XNH-21" vertex="1">
+          <mxGeometry y="30" width="170" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-6" value="Specialization" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" parent="HwPxVh02NSwoOAiZ7XNH-21" vertex="1">
+          <mxGeometry y="60" width="170" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-28" value="&amp;nbsp;engineers [ ]" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="HwPxVh02NSwoOAiZ7XNH-21" vertex="1">
+          <mxGeometry y="90" width="170" height="26" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-27" value="creates" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="336" y="324" width="60" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-30" value="engineer" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="1518" y="525" width="140" height="60" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-35" value="person" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="1277" y="93" width="140" height="86" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-39" value="Surname" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="HwPxVh02NSwoOAiZ7XNH-35" vertex="1">
+          <mxGeometry y="30" width="140" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="zkfFHV4jXpPFQw0GAbJ--18" value="Name" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="HwPxVh02NSwoOAiZ7XNH-35" vertex="1">
+          <mxGeometry y="60" width="140" height="26" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-43" value="manager" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="1533" y="633" width="140" height="60" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-55" value="" style="endArrow=none;html=1;rounded=0;exitX=-0.006;exitY=0.533;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="HwPxVh02NSwoOAiZ7XNH-53" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="396" y="136" as="sourcePoint" />
+            <mxPoint x="306" y="136" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-56" value="drives" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="316" y="114" width="60" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-57" value="0..1" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="296" y="107" width="40" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-58" value="1" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="366" y="107" width="30" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-61" value="" style="endArrow=none;html=1;rounded=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;exitX=1;exitY=0.75;exitDx=0;exitDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--17" target="zkfFHV4jXpPFQw0GAbJ--17" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="306" y="436" as="sourcePoint" />
+            <mxPoint x="386" y="216" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-64" value="" style="endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;exitX=0.75;exitY=0;exitDx=0;exitDy=0;fontColor=#B5739D;entryX=0.271;entryY=1.1;entryDx=0;entryDy=0;entryPerimeter=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="HwPxVh02NSwoOAiZ7XNH-10" target="HwPxVh02NSwoOAiZ7XNH-63" edge="1">
+          <mxGeometry width="160" relative="1" as="geometry">
+            <mxPoint x="246" y="406" as="sourcePoint" />
+            <mxPoint x="179.07999999999993" y="429" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-65" value="" style="endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.329;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;fontColor=#B5739D;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="HwPxVh02NSwoOAiZ7XNH-14" target="HwPxVh02NSwoOAiZ7XNH-63" edge="1">
+          <mxGeometry width="160" relative="1" as="geometry">
+            <mxPoint x="256" y="416" as="sourcePoint" />
+            <mxPoint x="192.94000000000005" y="429.99" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-66" value="" style="endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;exitX=0.25;exitY=0;exitDx=0;exitDy=0;entryX=0.524;entryY=1.1;entryDx=0;entryDy=0;entryPerimeter=0;fontColor=#B5739D;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="HwPxVh02NSwoOAiZ7XNH-16" target="HwPxVh02NSwoOAiZ7XNH-63" edge="1">
+          <mxGeometry width="160" relative="1" as="geometry">
+            <mxPoint x="266" y="426" as="sourcePoint" />
+            <mxPoint x="245.01999999999998" y="429.99" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-70" value="" style="endArrow=none;html=1;rounded=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;exitX=0.338;exitY=-0.022;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="HwPxVh02NSwoOAiZ7XNH-4" target="zkfFHV4jXpPFQw0GAbJ--0" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="306" y="436" as="sourcePoint" />
+            <mxPoint x="356" y="386" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-71" value="&lt;b style=&quot;font-size: 22px;&quot;&gt;&lt;font style=&quot;font-size: 22px;&quot;&gt;...&lt;/font&gt;&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=22;fontColor=#B5739D;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="330" y="463" width="40" height="40" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-76" value="" style="endArrow=none;html=1;rounded=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;exitX=1;exitY=0.75;exitDx=0;exitDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--0" target="zkfFHV4jXpPFQw0GAbJ--0" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="306" y="286" as="sourcePoint" />
+            <mxPoint x="356" y="236" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-79" value="1" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="176" y="188" width="30" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-80" value="n" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="181" y="246" width="30" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-83" value="" style="endArrow=none;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=-0.024;exitY=0.167;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="2gj3LCoahcwoFIzxKnGv-6" target="2gj3LCoahcwoFIzxKnGv-4" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="303" y="365" as="sourcePoint" />
+            <mxPoint x="353" y="315" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-91" value="&lt;font style=&quot;font-size: 23px;&quot;&gt;&lt;b&gt;?&lt;/b&gt;&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="1422" y="123" width="32" height="40" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-100" value="Manager" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="43" y="722" width="30" height="60" as="geometry" />
+        </mxCell>
+        <mxCell id="HwPxVh02NSwoOAiZ7XNH-101" value="Engineer" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="43" y="992" width="30" height="60" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-0" value="1" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="392" y="321" width="30" height="30" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-1" value="0..n" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="311" y="321" width="38" height="26" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-8" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="88" y="744" as="sourcePoint" />
+            <mxPoint x="344" y="662" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-9" value="Change car&#39;s components" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="357" y="640" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-10" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="83" y="752" as="sourcePoint" />
+            <mxPoint x="344" y="727" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-11" value="View/edit list of drivers" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="357" y="746" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-12" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="86" y="760" as="sourcePoint" />
+            <mxPoint x="349" y="770" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-13" value="Change car&#39;s main driver" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="355" y="794" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-14" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="86" y="769" as="sourcePoint" />
+            <mxPoint x="349" y="859" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-15" value="Visualize cars" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="357" y="843" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-16" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="87" y="779" as="sourcePoint" />
+            <mxPoint x="351" y="913" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-17" value="Create/edit/view Departmets" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="355" y="943" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-18" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="91" y="765" as="sourcePoint" />
+            <mxPoint x="347" y="815" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-19" value="Edit list of engineers in a department" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="355.5" y="893" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-20" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="90" y="788" as="sourcePoint" />
+            <mxPoint x="345" y="958" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-21" value="Hire new drivers" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="357" y="697" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-26" value="Create new component" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="351" y="1029" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-27" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="97" y="1035" as="sourcePoint" />
+            <mxPoint x="343" y="1048" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-28" value="Notify manager" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="652" y="1029" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-29" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="508" y="1050" as="sourcePoint" />
+            <mxPoint x="645" y="1048" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-30" value="&amp;lt;&amp;lt;include&amp;gt;&amp;gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="525" y="1021" width="84" height="26" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-31" value="Notify engineers" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="659" y="641" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-32" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="515" y="662" as="sourcePoint" />
+            <mxPoint x="652" y="660" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-33" value="&amp;lt;&amp;lt;include&amp;gt;&amp;gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="532" y="633" width="84" height="26" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-34" value="Person" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="43" y="1116" width="30" height="60" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-35" value="Apply for driver position" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="347" y="1137" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-36" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="97" y="1159" as="sourcePoint" />
+            <mxPoint x="336" y="1157" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-37" value="Notify manager" style="ellipse;whiteSpace=wrap;html=1;strokeWidth=2;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="646" y="1137" width="149" height="42" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-38" value="" style="endArrow=classic;html=1;rounded=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" edge="1">
+          <mxGeometry width="50" height="50" relative="1" as="geometry">
+            <mxPoint x="502" y="1158" as="sourcePoint" />
+            <mxPoint x="639" y="1156" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-39" value="&amp;lt;&amp;lt;include&amp;gt;&amp;gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="519" y="1129" width="84" height="26" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-40" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeWidth=4;strokeColor=#001DBC;fontColor=#ffffff;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="7" y="7" width="586" height="534" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-41" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeWidth=4;strokeColor=#B09500;fontColor=#000000;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="614" y="82.5" width="203" height="118" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-42" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeWidth=4;strokeColor=#B20000;fontColor=#ffffff;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="614" y="224.5" width="203" height="118" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-43" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeWidth=4;strokeColor=#d6b656;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="614" y="365.5" width="203" height="118" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-44" value="&lt;font size=&quot;1&quot; style=&quot;&quot;&gt;&lt;b style=&quot;font-size: 20px;&quot;&gt;Core Module&lt;/b&gt;&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="49" y="24" width="139" height="36" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-45" value="&lt;font size=&quot;1&quot; style=&quot;&quot;&gt;&lt;b style=&quot;font-size: 20px;&quot;&gt;Notifications Module&lt;/b&gt;&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="608" y="123.5" width="215" height="36" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-46" value="&lt;font size=&quot;1&quot; style=&quot;&quot;&gt;&lt;b style=&quot;font-size: 20px;&quot;&gt;Visualization Module&lt;/b&gt;&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="608" y="265.5" width="215" height="36" as="geometry" />
+        </mxCell>
+        <mxCell id="2gj3LCoahcwoFIzxKnGv-48" value="&lt;font size=&quot;1&quot; style=&quot;&quot;&gt;&lt;b style=&quot;font-size: 20px;&quot;&gt;&amp;nbsp;Applications Module&lt;/b&gt;&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rotation=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
+          <mxGeometry x="600" y="403.5" width="219" height="36" as="geometry" />
+        </mxCell>
+        <mxCell id="UvfdVoFo5HVwESIDpEAG-2" value="Formula-Team-Managment" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
+          <mxGeometry x="505" y="569" width="160" height="30" as="geometry" />
+        </mxCell>
+      </root>
+    </mxGraphModel>
+  </diagram>
+</mxfile>
diff --git a/diagrams/use_case_diagram.png b/diagrams/use_case_diagram.png
new file mode 100644
index 0000000000000000000000000000000000000000..f4338966beea08cd8871985f2ea666c41ebcc8a8
Binary files /dev/null and b/diagrams/use_case_diagram.png differ
diff --git a/notification/openapi.yaml b/notification/openapi.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b212e96299d64ab3f266e3b086f2f6fa8611ef5e
--- /dev/null
+++ b/notification/openapi.yaml
@@ -0,0 +1,262 @@
+openapi: 3.0.1
+info:
+  title: Notification module
+  description: |
+    Hand-made OpenAPI document for Notification module.
+  version: 1.0.0
+servers:
+  - url: "http://localhost:8083"
+tags:
+  - name: NotificationService
+components:
+  schemas:
+    NotificationDto:
+      type: object
+      title: Notification
+      description: Notification to be send to receivers
+      required:
+        - message
+        - receivers
+        - subject
+      properties:
+        message:
+          type: string
+          description: Content of the email
+        receivers:
+          type: array
+          items:
+            type: string
+          description: Email addresses the notification is sent to
+        subject:
+          type: string
+          description: Subject of the email
+    CarComponentNotificationDto:
+      type: object
+      title: CarComponentNotification
+      description: Notification about new component
+      required:
+        - carComponent
+        - receivers
+      properties:
+        carComponent:
+          $ref: "#/components/schemas/CarComponentDto"
+        receivers:
+          type: array
+          items:
+            type: string
+          description: Email addresses the notification is sent to
+    ApplicationNotificationDto:
+      type: object
+      title: ApplicationNotification
+      description: Application notification
+      required:
+        - application
+        - receivers
+      properties:
+        application:
+          $ref: "#/components/schemas/ApplicationDto"
+        receivers:
+          type: array
+          items:
+            type: string
+          description: Email addresses the notification is sent to
+    ConfirmationDto:
+      type: object
+      title: Confirmation
+      properties:
+        message:
+          type: string
+        subject:
+          type: string
+          description: Subject of the email
+        receivers:
+          type: array
+          items:
+            type: string
+          description: Email addresses the notification was sent to
+        sender:
+          type: string
+          description: Email address from which the notification was sent
+
+    CarComponentType:
+      type: string
+      description: Type of a component
+      enum:
+        - CHASSIS
+        - ENGINE
+        - FRONTWING
+        - SUSPENSION
+    CarComponentDto:
+      type: object
+      title: Component
+      description: Component of a car
+      required:
+        - id
+        - componentType
+        - information
+      properties:
+        id:
+          type: integer
+          format: int64
+          description: Id of the component
+          example: 1
+        componentType:
+          $ref: '#/components/schemas/CarComponentType'
+        weight:
+          type: number
+          description: Weight of the component
+          example: 50.5
+        information:
+          type: string
+          description: Specific information about component
+          example: lightweight front wing v2 (black)
+
+    ApplicationStatus:
+      type: string
+      description: Status of application
+      enum:
+        - pending
+        - rejected
+        - accepted
+
+    ApplicationDto:
+      type: object
+      title: Application
+      description: Application for becoming driver
+      required:
+        - id
+        - name
+        - surname
+        - fillingOutDate
+        - email
+      properties:
+        id:
+          type: integer
+          description: Id of the application
+          example: 1
+          format: int64
+        name:
+          type: string
+          description: Name of the person
+          example: Max
+        surname:
+          type: string
+          description: Surname of the person
+          example: Verstappen
+        birthday:
+          type: string
+          description: Date of birth
+          example: 09-30-1997
+          format: date
+        email:
+          type: string
+          description: Email address of the person
+        fillingOutDate:
+          type: string
+          description: Date of filling out the application
+          example: 09-30-1997
+          format: date
+        status:
+          $ref: '#/components/schemas/ApplicationStatus'
+  responses:
+    Unexpected:
+      description: Unexpected error
+      content:
+        application/json:
+          schema:
+            type: object
+            title: Unexpected
+            properties:
+              message:
+                type: string
+
+paths:
+  /notification:
+    post:
+      tags:
+        - NotificationService
+      summary: Creates and sends notification(s)
+      operationId: notify
+      requestBody:
+        required: true
+        description: Request body with receiver and message for new notification
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/NotificationDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ConfirmationDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /notification/component:
+    post:
+      tags:
+        - NotificationService
+      summary: Creates and sends notification(s) about new component
+      operationId: notifyNewComponent
+      requestBody:
+        required: true
+        description: Request body with receiver and car component for new notification
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/CarComponentNotificationDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ConfirmationDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /notification/application/new:
+    post:
+      tags:
+        - NotificationService
+      summary: Creates and sends notification(s)
+      operationId: notifyNewApplication
+      requestBody:
+        required: true
+        description: Request body with receiver and message for new notification
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/ApplicationNotificationDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ConfirmationDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+  /notification/application/status:
+    post:
+      tags:
+        - NotificationService
+      summary: Creates and sends notification
+      operationId: notifyApplicationStatusChange
+      requestBody:
+        required: true
+        description: Request body with receiver and message for new notification
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/ApplicationNotificationDto'
+      responses:
+        "200":
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/ConfirmationDto'
+        default:
+          $ref: '#/components/responses/Unexpected'
+
diff --git a/notification/pom.xml b/notification/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4d4ad7051513585c4a1a475fa39e1aa0f5976d64
--- /dev/null
+++ b/notification/pom.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>formula-team-management</artifactId>
+        <groupId>cz.muni.pa165</groupId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>notification</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>Notification module</name>
+    <description>Notification generated using OpenAPI Generator "java"</description>
+
+    <build>
+        <defaultGoal>spring-boot:run</defaultGoal>
+        <finalName>notification_module</finalName>
+
+        <plugins>
+            <plugin>
+                <groupId>org.openapitools</groupId>
+                <artifactId>openapi-generator-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            <inputSpec>${project.basedir}/openapi.yaml</inputSpec>
+                            <generatorName>spring</generatorName>
+                            <apiPackage>cz.muni.pa165.generated.api</apiPackage>
+                            <modelPackage>cz.muni.pa165.generated.model</modelPackage>
+                            <configOptions>
+                                <basePackage>cz.muni.pa165</basePackage>
+                                <configPackage>cz.muni.pa165.generated.config</configPackage>
+                                <useSpringBoot3>true</useSpringBoot3>
+                                <useTags>true</useTags>
+                                <delegatePattern>true</delegatePattern>
+                            </configOptions>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <executable>true</executable>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.validation</groupId>
+            <artifactId>jakarta.validation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-models-jakarta</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations-jakarta</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openapitools</groupId>
+            <artifactId>jackson-databind-nullable</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+            <version>3.0.4</version>
+        </dependency>
+
+        <!--		Actuator dependency-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+
+        <!--		Prometheus dependency -->
+        <dependency>
+            <groupId>io.micrometer</groupId>
+            <artifactId>micrometer-registry-prometheus</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/notification/src/main/java/cz/muni/pa165/config/MailConfig.java b/notification/src/main/java/cz/muni/pa165/config/MailConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..1faf9d05104fa0a238b3f58475712c45c1a561fb
--- /dev/null
+++ b/notification/src/main/java/cz/muni/pa165/config/MailConfig.java
@@ -0,0 +1,54 @@
+package cz.muni.pa165.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.ResourceBundleMessageSource;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.spring6.SpringTemplateEngine;
+import org.thymeleaf.templatemode.TemplateMode;
+import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
+import org.thymeleaf.templateresolver.ITemplateResolver;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Properties;
+
+@Configuration
+public class MailConfig {
+    @Bean
+    public JavaMailSender javaMailSender() {
+        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
+        mailSender.setHost("smtp.gmail.com");
+        mailSender.setPort(587);
+
+        mailSender.setUsername("formula.team.management@gmail.com");
+        mailSender.setPassword("cwnlsvcodawlphvu");
+
+        Properties props = mailSender.getJavaMailProperties();
+        props.put("mail.transport.protocol", "smtp");
+        props.put("mail.smtp.auth", "true");
+        props.put("mail.smtp.starttls.enable", "true");
+        props.put("mail.debug", "true");
+
+        return mailSender;
+    }
+
+    @Bean
+    public TemplateEngine emailTemplateEngine() {
+        final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
+        templateEngine.addTemplateResolver(htmlTemplateResolver());
+        return templateEngine;
+    }
+
+    private ITemplateResolver htmlTemplateResolver() {
+        final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
+        templateResolver.setPrefix("/templates/");
+        templateResolver.setSuffix(".html");
+        templateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
+        templateResolver.setTemplateMode(TemplateMode.HTML);
+        templateResolver.setCacheable(false);
+        return templateResolver;
+    }
+}
\ No newline at end of file
diff --git a/notification/src/main/java/cz/muni/pa165/exceptions/EmailException.java b/notification/src/main/java/cz/muni/pa165/exceptions/EmailException.java
new file mode 100644
index 0000000000000000000000000000000000000000..660b6625f26ecfe10c3e86ac9e2364d65e1f7ec3
--- /dev/null
+++ b/notification/src/main/java/cz/muni/pa165/exceptions/EmailException.java
@@ -0,0 +1,10 @@
+package cz.muni.pa165.exceptions;
+
+/**
+ * @author Michal Badin
+ */
+public class EmailException extends RuntimeException {
+    public EmailException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/notification/src/main/java/cz/muni/pa165/facade/NotificationFacade.java b/notification/src/main/java/cz/muni/pa165/facade/NotificationFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..8f802001e0360df82c238376ed98824ce4215da0
--- /dev/null
+++ b/notification/src/main/java/cz/muni/pa165/facade/NotificationFacade.java
@@ -0,0 +1,38 @@
+package cz.muni.pa165.facade;
+
+import cz.muni.pa165.generated.model.ApplicationNotificationDto;
+import cz.muni.pa165.generated.model.CarComponentNotificationDto;
+import cz.muni.pa165.generated.model.ConfirmationDto;
+import cz.muni.pa165.generated.model.NotificationDto;
+import cz.muni.pa165.service.NotificationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class NotificationFacade {
+
+    private final NotificationService notificationService;
+
+    @Autowired
+    public NotificationFacade(NotificationService notificationService) {
+
+        this.notificationService = notificationService;
+    }
+
+    public ConfirmationDto notify(NotificationDto notificationDto) {
+        return notificationService.notify(notificationDto);
+    }
+
+    public ConfirmationDto notifyNewComponent(CarComponentNotificationDto carComponentNotificationDto) {
+        return notificationService.notifyNewComponent(carComponentNotificationDto);
+    }
+
+    public ConfirmationDto notifyNewApplication(ApplicationNotificationDto applicationDto) {
+        return notificationService.notifyNewApplication(applicationDto);
+    }
+
+    public ConfirmationDto notifyApplicationStatusChange(ApplicationNotificationDto applicationDto) {
+        return notificationService.notifyApplicationStatusChange(applicationDto);
+
+    }
+}
diff --git a/notification/src/main/java/cz/muni/pa165/rest/NotificationController.java b/notification/src/main/java/cz/muni/pa165/rest/NotificationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..975e713dff0051c480f41672c3fd9e8b2d1a9f89
--- /dev/null
+++ b/notification/src/main/java/cz/muni/pa165/rest/NotificationController.java
@@ -0,0 +1,58 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.facade.NotificationFacade;
+import cz.muni.pa165.generated.api.NotificationServiceApiDelegate;
+import cz.muni.pa165.generated.model.ApplicationNotificationDto;
+import cz.muni.pa165.generated.model.CarComponentNotificationDto;
+import cz.muni.pa165.generated.model.ConfirmationDto;
+import cz.muni.pa165.generated.model.NotificationDto;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Alžbeta Hajná
+ */
+
+@Component
+public class NotificationController implements NotificationServiceApiDelegate {
+
+    private static final Logger log = LoggerFactory.getLogger(NotificationController.class);
+    private final NotificationFacade notificationFacade;
+
+    @Autowired
+    public NotificationController(NotificationFacade notificationFacade) {
+        this.notificationFacade = notificationFacade;
+    }
+
+    @Override
+    public ResponseEntity<ConfirmationDto> notify(NotificationDto notificationDto) {
+        log.debug("notify() called");
+
+        return new ResponseEntity<>(notificationFacade.notify(notificationDto), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ConfirmationDto> notifyNewComponent(CarComponentNotificationDto carComponentNotificationDto) {
+        log.debug("notifyComponentChange() called");
+
+        return new ResponseEntity<>(notificationFacade.notifyNewComponent(carComponentNotificationDto), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ConfirmationDto> notifyNewApplication(ApplicationNotificationDto applicationDto) {
+        log.debug("notifyNewApplication() called");
+
+        return new ResponseEntity<>(notificationFacade.notifyNewApplication(applicationDto), HttpStatus.OK);
+    }
+
+    @Override
+    public ResponseEntity<ConfirmationDto> notifyApplicationStatusChange(ApplicationNotificationDto applicationDto) {
+        log.debug("notifyApplicationStatusChange() called");
+
+        return new ResponseEntity<>(notificationFacade.notifyApplicationStatusChange(applicationDto), HttpStatus.OK);
+    }
+}
diff --git a/notification/src/main/java/cz/muni/pa165/rest/exceptionhandling/ApiError.java b/notification/src/main/java/cz/muni/pa165/rest/exceptionhandling/ApiError.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e1161dcad4805649ce3757c0c7983dd52f30e9c
--- /dev/null
+++ b/notification/src/main/java/cz/muni/pa165/rest/exceptionhandling/ApiError.java
@@ -0,0 +1,62 @@
+package cz.muni.pa165.rest.exceptionhandling;
+
+import org.springframework.http.HttpStatus;
+
+import java.time.LocalDateTime;
+
+public class ApiError {
+
+    private LocalDateTime timestamp;
+    private HttpStatus status;
+    private String message;
+    private String path;
+
+    public ApiError(LocalDateTime timestamp, HttpStatus status, String message, String path) {
+        this.timestamp = timestamp;
+        this.status = status;
+        this.message = message;
+        this.path = path;
+    }
+
+    public LocalDateTime getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(LocalDateTime timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public HttpStatus getStatus() {
+        return status;
+    }
+
+    public void setStatus(HttpStatus status) {
+        this.status = status;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    @Override
+    public String toString() {
+        return "ApiError{" +
+                "timestamp=" + timestamp +
+                ", status=" + status +
+                ", message='" + message + '\'' +
+                ", path='" + path + '\'' +
+                '}';
+    }
+}
diff --git a/notification/src/main/java/cz/muni/pa165/rest/exceptionhandling/CustomRestGlobalExceptionHandling.java b/notification/src/main/java/cz/muni/pa165/rest/exceptionhandling/CustomRestGlobalExceptionHandling.java
new file mode 100644
index 0000000000000000000000000000000000000000..323b98f637b01a42b134dcd22cb77f5c6dba82f0
--- /dev/null
+++ b/notification/src/main/java/cz/muni/pa165/rest/exceptionhandling/CustomRestGlobalExceptionHandling.java
@@ -0,0 +1,35 @@
+package cz.muni.pa165.rest.exceptionhandling;
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.util.UrlPathHelper;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+
+@RestControllerAdvice
+public class CustomRestGlobalExceptionHandling {
+
+    private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper();
+
+    @ExceptionHandler({Exception.class})
+    public ResponseEntity<ApiError> handleAll(final Exception ex, HttpServletRequest request) {
+        final ApiError apiError = new ApiError(
+                LocalDateTime.now(Clock.systemUTC()),
+                HttpStatus.INTERNAL_SERVER_ERROR,
+                getInitialException(ex).getLocalizedMessage(),
+                URL_PATH_HELPER.getRequestUri(request));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    private Exception getInitialException(Exception ex) {
+        while (ex.getCause() != null) {
+            ex = (Exception) ex.getCause();
+        }
+        return ex;
+    }
+}
diff --git a/notification/src/main/java/cz/muni/pa165/service/NotificationService.java b/notification/src/main/java/cz/muni/pa165/service/NotificationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..542f03d03dcd5ff878e342173559e33ad8d731b9
--- /dev/null
+++ b/notification/src/main/java/cz/muni/pa165/service/NotificationService.java
@@ -0,0 +1,113 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.exceptions.EmailException;
+import cz.muni.pa165.generated.model.*;
+import jakarta.mail.MessagingException;
+import jakarta.mail.internet.MimeMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Service;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.context.Context;
+
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+@Service
+public class NotificationService {
+    private static final String EMAIL = "formula.team.management@gmail.com";
+    private static final Logger log = LoggerFactory.getLogger(NotificationService.class);
+    private final JavaMailSender emailSender;
+    private final TemplateEngine templateEngine;
+
+    @Autowired
+    public NotificationService(JavaMailSender emailSender, TemplateEngine templateEngine) {
+        this.emailSender = emailSender;
+        this.templateEngine = templateEngine;
+    }
+
+    public ConfirmationDto notify(NotificationDto notificationDto) {
+        return sendEmail(notificationDto.getSubject(), notificationDto.getMessage(), notificationDto.getReceivers());
+    }
+
+    public ConfirmationDto notifyNewComponent(CarComponentNotificationDto carComponentNotificationDto) {
+        var c = carComponentNotificationDto.getCarComponent();
+        var subject = "New component: " + c.getInformation();
+
+        Context ctx = new Context();
+        ctx.setVariable("id", c.getId());
+        ctx.setVariable("type", c.getComponentType());
+        ctx.setVariable("info", c.getInformation());
+        ctx.setVariable("weight", c.getWeight());
+
+        var htmlContent = templateEngine.process("new-component-email", ctx);
+
+        return sendEmail(subject, htmlContent, carComponentNotificationDto.getReceivers());
+    }
+
+    public ConfirmationDto notifyNewApplication(ApplicationNotificationDto applicationNotificationDto) {
+        var a = applicationNotificationDto.getApplication();
+        var subject = "New application: " + getFullName(a);
+
+        Context ctx = new Context();
+        ctx.setVariable("id", a.getId());
+        ctx.setVariable("name", a.getName());
+        ctx.setVariable("surname", a.getSurname());
+        ctx.setVariable("birthday", a.getBirthday().format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
+        ctx.setVariable("email", a.getEmail());
+        ctx.setVariable("fillingOutDate", a.getFillingOutDate().format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
+
+        var htmlContent = templateEngine.process("new-application-email", ctx);
+
+        return sendEmail(subject, htmlContent, applicationNotificationDto.getReceivers());
+    }
+
+    public ConfirmationDto notifyApplicationStatusChange(ApplicationNotificationDto applicationNotificationDto) {
+        var a = applicationNotificationDto.getApplication();
+        var subject = "Application for Formula Driver Position";
+        var htmlContent = "";
+
+        Context ctx = new Context();
+        ctx.setVariable("fullName", getFullName(a));
+        ctx.setVariable("today", LocalDate.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
+
+        if (a.getStatus() == ApplicationStatus.ACCEPTED) {
+            htmlContent = templateEngine.process("accepted-email", ctx);
+        } else {
+            htmlContent = templateEngine.process("rejected-email", ctx);
+        }
+
+        return sendEmail(subject, htmlContent, applicationNotificationDto.getReceivers());
+    }
+
+    private ConfirmationDto sendEmail(String subject, String text, List<String> receivers) {
+        MimeMessage message = emailSender.createMimeMessage();
+
+        try {
+            MimeMessageHelper helper = new MimeMessageHelper(message, true, StandardCharsets.UTF_8.name());
+            helper.setFrom(EMAIL);
+            helper.setTo(receivers.toArray(String[]::new));
+            helper.setSubject(subject);
+            helper.setText(text, true);
+
+            emailSender.send(message);
+
+            return new ConfirmationDto()
+                    .sender(EMAIL)
+                    .receivers(receivers)
+                    .subject(subject)
+                    .message(text);
+        } catch (MessagingException e) {
+            throw new EmailException("Failed to send email", e);
+        }
+    }
+
+    private String getFullName(ApplicationDto applicationDto) {
+        return applicationDto.getName() + " " + applicationDto.getSurname();
+    }
+}
diff --git a/notification/src/main/resources/application.properties b/notification/src/main/resources/application.properties
new file mode 100644
index 0000000000000000000000000000000000000000..e0cdf8282c04067df800df2f8086bc69686d291f
--- /dev/null
+++ b/notification/src/main/resources/application.properties
@@ -0,0 +1,14 @@
+server.port=8083
+spring.mail.host=smtp.gmail.com
+spring.mail.port=587
+spring.mail.username=formula.team.management@gmail.com
+spring.mail.password=cwnlsvcodawlphvu
+spring.mail.protocol=smtps
+spring.mail.properties.mail.from.email=formula.team.management@gmail.com
+spring.mail.properties.mail.smtp.auth=true
+spring.mail.properties.mail.smtp.starttls.enable=false
+
+management.endpoints.web.exposure.include=health,metrics,loggers,beans,prometheus
+management.endpoint.health.show-details=always
+management.endpoint.health.show-components=always
+management.endpoint.health.probes.enabled=true
\ No newline at end of file
diff --git a/notification/src/main/resources/templates/accepted-email.html b/notification/src/main/resources/templates/accepted-email.html
new file mode 100644
index 0000000000000000000000000000000000000000..4f41dcbc558579c8ebded12ffb5e3df61cbe29c5
--- /dev/null
+++ b/notification/src/main/resources/templates/accepted-email.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org" lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>Application for Formula Driver Position</title>
+        <style>
+            body {
+                font-family: Arial, sans-serif;
+                margin: 0;
+                padding: 0;
+                background-color: #f6f6f6;
+            }
+
+            .header {
+                display: flex;
+                flex-direction: row;
+                justify-content: space-between;
+                align-items: center;
+            }
+
+            .header-right {
+                margin-left: auto;
+            }
+
+            .container {
+                max-width: 625px;
+                margin: 0 auto;
+                background-color: #fff;
+                padding: 20px;
+                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+                position: relative;
+            }
+
+            h1 {
+                font-size: 24px;
+                font-weight: bold;
+                margin: 0;
+                text-align: left;
+            }
+
+            .date {
+                font-size: 14px;
+                font-weight: normal;
+                margin: 0;
+            }
+
+            .location {
+                font-size: 14px;
+                font-weight: normal;
+                margin: 0;
+            }
+
+            p {
+                font-size: 16px;
+                line-height: 1.5;
+                margin: 20px 0;
+                text-align: justify;
+            }
+
+            .signature {
+                font-size: 14px;
+                font-weight: normal;
+                margin: 50px 0 0 0;
+                text-align: right;
+            }
+        </style>
+    </head>
+    <body>
+        <div class="container">
+            <div class="header">
+                <h1>Application for Formula Driver Position</h1>
+                <div class="header-right">
+                    <div class="date" th:text="${today}"></div>
+                    <div class="location">Brno, Czech Republic</div>
+                </div>
+            </div>
+
+            <p><b>Dear <span th:text="${fullName}"></span>,</b></p>
+            <p>I am pleased to inform you that your application for the position of Formula Driver has been <b>Accepted</b>. We are excited to have you join our team and look forward to working with you.</p>
+            <p>Your experience, skills, and passion for racing make you an excellent fit for our team. We believe that you will be a valuable asset to our organization and we are eager to see what you can accomplish on the track.</p>
+            <p>As a next step, we will contact you shortly to discuss the details of your employment contract and to schedule your training and orientation. In the meantime, please do not hesitate to contact us if you have any questions or concerns.</p>
+            <p>Thank you for your interest in our team and for taking the time to apply. We look forward to a long and successful partnership with you.</p>
+            <p>Best regards,
+                <br>Formula Team Management</p>
+            <div class="signature">-- Formula Team Management</div>
+        </div>
+    </body>
+</html>
\ No newline at end of file
diff --git a/notification/src/main/resources/templates/new-application-email.html b/notification/src/main/resources/templates/new-application-email.html
new file mode 100644
index 0000000000000000000000000000000000000000..9f13d218ea7647d79f88b5c89e65169017367e57
--- /dev/null
+++ b/notification/src/main/resources/templates/new-application-email.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org" lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>Application for Formula Driver Position</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            margin: 0;
+            padding: 0;
+            background-color: #f6f6f6;
+        }
+
+        .container {
+            max-width: 625px;
+            margin: 0 auto;
+            background-color: #fff;
+            padding: 20px;
+            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+            position: relative;
+        }
+
+        h1 {
+            font-size: 24px;
+            font-weight: bold;
+            margin: 0;
+            text-align: left;
+        }
+
+        p {
+            font-size: 16px;
+            line-height: 1.5;
+            margin: 20px 0;
+            text-align: justify;
+        }
+    </style>
+</head>
+<body>
+<div class="container">
+    <h1>New Application Received</h1>
+    <table>
+        <tr>
+            <td>Id:</td>
+            <td th:text="${id}"></td>
+        </tr>
+        <tr>
+            <td>Name:</td>
+            <td th:text="${name}"></td>
+        </tr>
+        <tr>
+            <td>Surname:</td>
+            <td th:text="${surname}"></td>
+        </tr>
+        <tr>
+            <td>Birthday:</td>
+            <td th:text="${birthday}"></td>
+        </tr>
+        <tr>
+            <td>Email:</td>
+            <td th:text="${email}"></td>
+        </tr>
+        <tr>
+            <td>Filling out date:</td>
+            <td th:text="${fillingOutDate}"></td>
+        </tr>
+    </table>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/notification/src/main/resources/templates/new-component-email.html b/notification/src/main/resources/templates/new-component-email.html
new file mode 100644
index 0000000000000000000000000000000000000000..cb066f1567c990e3f5883263a8fecbc0426b8c51
--- /dev/null
+++ b/notification/src/main/resources/templates/new-component-email.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org" lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>Application for Formula Driver Position</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            margin: 0;
+            padding: 0;
+            background-color: #f6f6f6;
+        }
+
+        .container {
+            max-width: 625px;
+            margin: 0 auto;
+            background-color: #fff;
+            padding: 20px;
+            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+            position: relative;
+        }
+
+        h1 {
+            font-size: 24px;
+            font-weight: bold;
+            margin: 0;
+            text-align: left;
+        }
+
+        p {
+            font-size: 16px;
+            line-height: 1.5;
+            margin: 20px 0;
+            text-align: justify;
+        }
+    </style>
+</head>
+<body>
+<div class="container">
+    <h1>New component was added to Formula team management core</h1>
+    <table>
+        <tr>
+            <td>Id:</td>
+            <td th:text="${id}"></td>
+        </tr>
+        <tr>
+            <td>Component type:</td>
+            <td th:text="${type}"></td>
+        </tr>
+        <tr>
+            <td>Information:</td>
+            <td th:text="${info}"></td>
+        </tr>
+        <tr>
+            <td>Weight:</td>
+            <td th:text="${weight} + 'kg'"></td>
+        </tr>
+    </table>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/notification/src/main/resources/templates/rejected-email.html b/notification/src/main/resources/templates/rejected-email.html
new file mode 100644
index 0000000000000000000000000000000000000000..bded181896be3cc83966a485054e8d1d5ef279e0
--- /dev/null
+++ b/notification/src/main/resources/templates/rejected-email.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org" lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>Application for Formula Driver Position</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            margin: 0;
+            padding: 0;
+            background-color: #f6f6f6;
+        }
+
+        .header {
+            display: flex;
+            flex-direction: row;
+            justify-content: space-between;
+            align-items: center;
+        }
+
+        .header-right {
+            margin-left: auto;
+        }
+
+        .container {
+            max-width: 625px;
+            margin: 0 auto;
+            background-color: #fff;
+            padding: 20px;
+            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+            position: relative;
+        }
+
+        h1 {
+            font-size: 24px;
+            font-weight: bold;
+            margin: 0;
+            text-align: left;
+        }
+
+        .date {
+            font-size: 14px;
+            font-weight: normal;
+            margin: 0;
+        }
+
+        .location {
+            font-size: 14px;
+            font-weight: normal;
+            margin: 0;
+        }
+
+        p {
+            font-size: 16px;
+            line-height: 1.5;
+            margin: 20px 0;
+            text-align: justify;
+        }
+
+        .signature {
+            font-size: 14px;
+            font-weight: normal;
+            margin: 50px 0 0 0;
+            text-align: right;
+        }
+    </style>
+</head>
+<body>
+<div class="container">
+    <div class="header">
+        <h1>Application for Formula Driver Position</h1>
+        <div class="header-right">
+            <div class="date" th:text="${today}"></div>
+            <div class="location">Brno, Czech Republic</div>
+        </div>
+    </div>
+
+    <p><b>Dear <span th:text="${fullName}"></span>,</b></p>
+    <p>I regret to inform you that your application for the Formula Driver position has been <b>Declined</b>. While we appreciate your interest in the role, we have decided to pursue other candidates whose skills and experience more closely match our requirements.</p>
+    <p>I want to personally thank you for taking the time to apply for the position and for your passion for motorsports. We would like to keep your resume on file in case any suitable openings arise in the future.</p>
+    <p>If you have any questions or would like feedback on your application, please feel free to reach out to us.</p>
+    <p>Thank you again for considering Formula Team Management, and we wish you all the best in your future endeavors.</p>
+    <p>Best regards,
+        <br>Formula Team Management</p>
+    <div class="signature">-- Formula Team Management</div>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/notification/src/test/java/cz/muni/pa165/rest/NotificationControllerTest.java b/notification/src/test/java/cz/muni/pa165/rest/NotificationControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..043d7ebfcc0274df100ea28d78daafd9fcac3231
--- /dev/null
+++ b/notification/src/test/java/cz/muni/pa165/rest/NotificationControllerTest.java
@@ -0,0 +1,57 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.facade.NotificationFacade;
+import cz.muni.pa165.generated.model.*;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.ResponseEntity;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class NotificationControllerTest {
+    private static final Logger log = LoggerFactory.getLogger(NotificationControllerTest.class);
+
+    private final NotificationFacade mockFacade = Mockito.mock(NotificationFacade.class);
+    private final NotificationController controller = new NotificationController(mockFacade);
+
+    @Test
+    void testNotifyNewComponent() {
+        log.debug("testNotifyNewComponent() running");
+
+        var dto = new CarComponentNotificationDto()
+                .carComponent(new CarComponentDto())
+                .receivers(List.of("aaa@aaa.com"));
+
+        controller.notifyNewComponent(dto);
+        Mockito.verify(mockFacade, Mockito.times(1)).notifyNewComponent(dto);
+    }
+
+    @Test
+    void testNotifyNewApplication() {
+        log.debug("testNotifyNewApplication() running");
+
+        var dto = new ApplicationNotificationDto()
+                .application(new ApplicationDto())
+                .receivers(List.of("aaa@aaa.com"));
+
+        controller.notifyNewApplication(dto);
+        Mockito.verify(mockFacade, Mockito.times(1)).notifyNewApplication(dto);
+    }
+
+    @Test
+    void testNotifyApplicationStatusChange() {
+        log.debug("testNotifyApplicationStatusChange() running");
+
+        var dto = new ApplicationNotificationDto()
+                .application(new ApplicationDto())
+                .receivers(List.of("aaa@aaa.com"));
+
+        controller.notifyApplicationStatusChange(dto);
+        Mockito.verify(mockFacade, Mockito.times(1)).notifyApplicationStatusChange(dto);
+    }
+}
diff --git a/notification/src/test/java/cz/muni/pa165/rest/NotificationIT.java b/notification/src/test/java/cz/muni/pa165/rest/NotificationIT.java
new file mode 100644
index 0000000000000000000000000000000000000000..9bb60eb71a5561d8b62b37c747686d0dec765306
--- /dev/null
+++ b/notification/src/test/java/cz/muni/pa165/rest/NotificationIT.java
@@ -0,0 +1,62 @@
+package cz.muni.pa165.rest;
+
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Integration tests. Run by "mvn verify / mvn test / mvn clean install".
+ * No need to test all the rest controllers logic here, just test that the endpoints are working
+ */
+@SpringBootTest
+@AutoConfigureMockMvc
+class NotificationIT {
+
+    private static final Logger log = LoggerFactory.getLogger(NotificationIT.class);
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @Test
+    void testNotificationModuleResponse() throws Exception {
+        log.info("testNotificationModuleResponse() running");
+
+        String testRequestContent = "{\"message\": \"string\",\"receivers\": [\"formula.team.management@gmail.com\"],\"subject\": \"string\"}";
+
+        String response = mockMvc.perform(
+                        post("/notification")
+                                .accept(MediaType.APPLICATION_JSON)
+                                .contentType(MediaType.APPLICATION_JSON)
+                                .content(testRequestContent))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+        log.info("response: {}", response);
+
+        String expectedResponse = "{\"message\":\"string\",\"subject\":\"string\",\"receivers\":[\"formula.team.management@gmail.com\"],\"sender\":\"formula.team.management@gmail.com\"}";
+
+        assertEquals(expectedResponse, response);
+    }
+
+    @Test
+    void testNotificationModuleNoRecipient() throws Exception {
+        log.info("testNotificationModuleResponse() running");
+
+        String testRequestContent = "{\"message\": \"string\",\"receivers\": [],\"subject\": \"string\"}";
+
+        mockMvc.perform(
+                post("/notification")
+                        .accept(MediaType.APPLICATION_JSON)
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(testRequestContent))
+                .andExpect(status().isInternalServerError());
+    }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 6484ceebd1934d12672ebe0dbeb4f47b29ca7e4b..4c6ef32a2ef065847866e9626a4d8b551df029cb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,108 +1,123 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
 
-	<!-- parent project -->
-	<parent>
-		<groupId>org.springframework.boot</groupId>
-		<artifactId>spring-boot-starter-parent</artifactId>
-		<version>3.0.4</version>
-	</parent>
+    <!-- parent project -->
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.0.3</version>
+        <relativePath/>
+    </parent>
 
-	<!-- this project -->
-	<groupId>cz.muni.pa165</groupId>
-	<artifactId>formula-team-management</artifactId>
-	<version>0.0.1-SNAPSHOT</version>
-	<packaging>pom</packaging>
-	<name>formula-team-management</name>
-	<description>Formula team management</description>
+    <!-- this project -->
+    <groupId>cz.muni.pa165</groupId>
+    <artifactId>formula-team-management</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>formula-team-management</name>
+    <description>Formula team management</description>
 
-	<!-- modules -->
-	<modules>
-		<module>core</module>
-	</modules>
+    <!-- modules -->
+    <modules>
+        <module>core</module>
+        <module>application</module>
+        <module>visualization</module>
+        <module>notification</module>
+    </modules>
 
-	<!-- properties are inherited into children projects and can be used anywhere with ${property} -->
-	<properties>
-		<java.version>17</java.version>
-		<swagger-jakarta-version>2.2.8</swagger-jakarta-version>
-	</properties>
+    <!-- properties are inherited into children projects and can be used anywhere with ${property} -->
+    <properties>
+        <java.version>17</java.version>
+        <swagger-jakarta-version>2.2.8</swagger-jakarta-version>
+        <org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
+    </properties>
 
-	<build>
-		<!-- sets that "mvn" alone means "mvn install" -->
-		<defaultGoal>install</defaultGoal>
+    <build>
+        <!-- sets that "mvn" alone means "mvn install" -->
+        <defaultGoal>install</defaultGoal>
 
-		<!--
+        <!--
             Sets versions of plugins for all modules in one place.
             This does not include the listed plugins into modules,
             modules still must be declared in each module that uses them,
             just without <version>.
          -->
-		<pluginManagement>
-			<plugins>
-				<plugin>
-					<groupId>org.openapitools</groupId>
-					<artifactId>openapi-generator-maven-plugin</artifactId>
-					<version>6.4.0</version>
-				</plugin>
-				<plugin>
-					<groupId>org.springdoc</groupId>
-					<artifactId>springdoc-openapi-maven-plugin</artifactId>
-					<version>1.4</version>
-				</plugin>
-			</plugins>
-		</pluginManagement>
-	</build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.openapitools</groupId>
+                    <artifactId>openapi-generator-maven-plugin</artifactId>
+                    <version>6.4.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.springdoc</groupId>
+                    <artifactId>springdoc-openapi-maven-plugin</artifactId>
+                    <version>1.4</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
 
-	<dependencyManagement>
-		<dependencies>
-			<dependency>
-				<groupId>org.springdoc</groupId>
-				<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
-				<version>2.0.2</version>
-			</dependency>
-			<dependency>
-				<groupId>org.apache.commons</groupId>
-				<artifactId>commons-text</artifactId>
-				<version>1.10.0</version>
-			</dependency>
-			<dependency>
-				<groupId>com.google.code.findbugs</groupId>
-				<artifactId>jsr305</artifactId>
-				<version>3.0.2</version>
-			</dependency>
-			<dependency>
-				<groupId>org.openapitools</groupId>
-				<artifactId>jackson-databind-nullable</artifactId>
-				<version>0.2.4</version>
-			</dependency>
-			<dependency>
-				<groupId>io.swagger</groupId>
-				<artifactId>swagger-annotations</artifactId>
-				<version>1.6.9</version>
-			</dependency>
-			<dependency>
-				<groupId>javax.validation</groupId>
-				<artifactId>validation-api</artifactId>
-				<version>2.0.1.Final</version>
-			</dependency>
-			<dependency>
-				<groupId>io.swagger.core.v3</groupId>
-				<artifactId>swagger-models-jakarta</artifactId>
-				<version>${swagger-jakarta-version}</version>
-			</dependency>
-			<dependency>
-				<groupId>io.swagger.core.v3</groupId>
-				<artifactId>swagger-annotations-jakarta</artifactId>
-				<version>${swagger-jakarta-version}</version>
-			</dependency>
-			<dependency>
-				<groupId>javax.annotation</groupId>
-				<artifactId>javax.annotation-api</artifactId>
-				<version>1.3.2</version>
-			</dependency>
-		</dependencies>
-	</dependencyManagement>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springdoc</groupId>
+                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+                <version>2.0.2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-text</artifactId>
+                <version>1.10.0</version>
+            </dependency>
+            <dependency>
+                <groupId>com.google.code.findbugs</groupId>
+                <artifactId>jsr305</artifactId>
+                <version>3.0.2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.openapitools</groupId>
+                <artifactId>jackson-databind-nullable</artifactId>
+                <version>0.2.4</version>
+            </dependency>
+            <dependency>
+                <groupId>io.swagger</groupId>
+                <artifactId>swagger-annotations</artifactId>
+                <version>1.6.9</version>
+            </dependency>
+            <dependency>
+                <groupId>javax.validation</groupId>
+                <artifactId>validation-api</artifactId>
+                <version>2.0.1.Final</version>
+            </dependency>
+            <dependency>
+                <groupId>io.swagger.core.v3</groupId>
+                <artifactId>swagger-models-jakarta</artifactId>
+                <version>${swagger-jakarta-version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.swagger.core.v3</groupId>
+                <artifactId>swagger-annotations-jakarta</artifactId>
+                <version>${swagger-jakarta-version}</version>
+            </dependency>
+            <dependency>
+                <groupId>javax.annotation</groupId>
+                <artifactId>javax.annotation-api</artifactId>
+                <version>1.3.2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mapstruct</groupId>
+                <artifactId>mapstruct</artifactId>
+                <version>${org.mapstruct.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mapstruct</groupId>
+                <artifactId>mapstruct-processor</artifactId>
+                <version>${org.mapstruct.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
 
 </project>
diff --git a/prometheus.yml b/prometheus.yml
new file mode 100644
index 0000000000000000000000000000000000000000..aedec200866b8ec2b054c2af419d09ed99560d84
--- /dev/null
+++ b/prometheus.yml
@@ -0,0 +1,17 @@
+global:
+  scrape_interval: 1s
+  external_labels:
+    monitor: 'my-monitor'
+
+scrape_configs:
+  - job_name: 'prometheus'
+    static_configs:
+      - targets: ['prometheus:9090']
+  - job_name: 'formula-core'
+    metrics_path: /actuator/prometheus
+    static_configs:
+      - targets:
+        - core:8090
+        - application:8081
+        - visualization:8082
+        - notification:8083
\ No newline at end of file
diff --git a/scenario-locust/scenario.py b/scenario-locust/scenario.py
new file mode 100644
index 0000000000000000000000000000000000000000..abbeff262096946c59db8351db6675065f728afc
--- /dev/null
+++ b/scenario-locust/scenario.py
@@ -0,0 +1,35 @@
+from locust import HttpUser, task
+import random
+
+class Admin(HttpUser):
+    fixed_count = 1
+
+    @task
+    def update_random_application_status(self):
+        response = self.client.get("/application")
+
+        if len(response.json()) == 0:
+            return
+
+        applicationNumber = random.choice([i for i in range(1, len(response.json()))])
+        status = random.choice(["accepted", "rejected"])
+        self.client.put(f"/application/status?id={applicationNumber}&applicationStatus={status}")
+
+    @task
+    def get_application(self):
+        self.client.get("/application?id=1")
+
+    @task
+    def get_all_application(self):
+        self.client.get("/application")
+
+
+class User(HttpUser):
+    @task
+    def create_application(self):
+        self.client.post("/application", json={
+            "name": "Jozko",
+            "surname": "Example",
+            "birthday": "2000-05-06",
+            "email": "john@example.com"
+        })
diff --git a/visualization/.gitignore b/visualization/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..549e00a2a96fa9d7c5dbc9859664a78d980158c2
--- /dev/null
+++ b/visualization/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/visualization/.mvn/wrapper/maven-wrapper.jar b/visualization/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000000000000000000000000000000000000..bf82ff01c6cdae4a1bb754a6e062954d77ac5c11
Binary files /dev/null and b/visualization/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/visualization/.mvn/wrapper/maven-wrapper.properties b/visualization/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000000000000000000000000000000000..ca5ab4bab121c408ae246f6ef5fae23b58db2d65
--- /dev/null
+++ b/visualization/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
diff --git a/visualization/mvnw b/visualization/mvnw
new file mode 100755
index 0000000000000000000000000000000000000000..8a8fb2282df5b8f7263470a5a2dc0e196f35f35f
--- /dev/null
+++ b/visualization/mvnw
@@ -0,0 +1,316 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /usr/local/etc/mavenrc ] ; then
+    . /usr/local/etc/mavenrc
+  fi
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`\\unset -f command; \\command -v java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    else
+      jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+        else
+            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+        fi
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl -o "$wrapperJarPath" "$jarUrl" -f
+        else
+            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+        fi
+
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  $MAVEN_DEBUG_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" \
+  "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/visualization/mvnw.cmd b/visualization/mvnw.cmd
new file mode 100644
index 0000000000000000000000000000000000000000..1d8ab018eaf11d9b3a4a90e7818ace373dfbb380
--- /dev/null
+++ b/visualization/mvnw.cmd
@@ -0,0 +1,188 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %DOWNLOAD_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+  %JVM_CONFIG_MAVEN_PROPS% ^
+  %MAVEN_OPTS% ^
+  %MAVEN_DEBUG_OPTS% ^
+  -classpath %WRAPPER_JAR% ^
+  "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/visualization/openapi.yaml b/visualization/openapi.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..f8093b631ffa1c63d182d7cee162ef9315ba4035
--- /dev/null
+++ b/visualization/openapi.yaml
@@ -0,0 +1,174 @@
+openapi: 3.0.1
+info:
+  title: Visualization module
+  description: |
+    Hand-made OpenAPI document.
+  version: 1.0.0
+servers:
+  - url: "http://localhost:8082"
+tags:
+  - name: Visualization
+components:
+  schemas:
+    CarDto:
+      type: object
+      title: Car
+      description: Formula
+      required:
+        - id
+      properties:
+        id:
+          type: integer
+          format: int64
+          description: Id of the car
+          example: 1
+        components:
+          type: array
+          description: List of components in the car
+          items:
+            $ref: '#/components/schemas/CarComponentDto'
+        driver:
+          $ref: '#/components/schemas/DriverDto'
+    CarComponentType:
+      type: string
+      description: Type of a component
+      enum:
+        - CHASSIS
+        - ENGINE
+        - FRONTWING
+        - SUSPENSION
+    CarComponentDto:
+      type: object
+      title: Component
+      description: Component of a car
+      required:
+        - id
+        - componentType
+        - information
+      properties:
+        id:
+          type: integer
+          format: int64
+          description: Id of the component
+          example: 1
+        componentType:
+          $ref: '#/components/schemas/CarComponentType'
+        weight:
+          type: number
+          description: Weight of the component
+          example: 50.5
+        information:
+          type: string
+          description: Specific information about component
+          example: lightweight front wing v2 (black)
+    Characteristic:
+      type: string
+      enum:
+        - aggressiveness
+        - driving on the wet
+        - react quickly
+        - remain completely focused
+        - adaptability to the environment
+        - strong neck
+        - ability to interact with the team
+        - efficient cardiovascular systems
+        - excellent when overtaking
+        - excellent starter
+        - excellent in the corners
+        - excellent in straight lines
+
+    DriverDto:
+      type: object
+      title: Driver
+      description: Driver of a car
+      required:
+        - id
+        - name
+        - surname
+        - nationality
+        - characteristics
+      properties:
+        id:
+          type: integer
+          format: int64
+          description: Id of the driver
+          example: 1
+        name:
+          type: string
+          description: Name of the driver
+          example: Max
+        surname:
+          type: string
+          description: Surname of the driver
+          example: Verstappen
+        height:
+          type: integer
+          description: Height in cm
+          example: 170
+        birthday:
+          type: string
+          description: Date of birth
+          format: date
+          example: 1997-09-30
+        nationality:
+          type: string
+          description: Nationality of the driver
+          example: Dutch
+        characteristics:
+          type: array
+          description: Set of driver's characteristics
+          items:
+            $ref: '#/components/schemas/Characteristic'
+
+  responses:
+    NotFound:
+      description: The specified resource was not found
+      content:
+        application/json:
+          schema:
+            type: object
+            title: NotFound
+            properties:
+              message:
+                type: string
+    Unexpected:
+      description: Unexpected error
+      content:
+        application/json:
+          schema:
+            type: object
+            title: Unexpected
+            properties:
+              message:
+                type: string
+
+paths:
+  /visualization:
+    post:
+      tags:
+        - Visualization
+      summary: Creates a visualization of given car
+      operationId: generateCarPdf
+      requestBody:
+        required: true
+        description: Request body with the car for visualization
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                car:
+                  $ref: '#/components/schemas/CarDto'
+              required:
+                - car
+      responses:
+        "200":
+          description: OK
+          content:
+            application/pdf:
+              schema:
+                type: string
+                format: binary
+        default:
+          $ref: '#/components/responses/Unexpected'
+
diff --git a/visualization/pom.xml b/visualization/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5f250842150f2f66797f9ba599e1d28b91eac0cf
--- /dev/null
+++ b/visualization/pom.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <!-- definition of parent Maven project -->
+    <parent>
+        <artifactId>formula-team-management</artifactId>
+        <groupId>cz.muni.pa165</groupId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <!-- this module definition -->
+    <artifactId>visualization</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>Visualization module</name>
+    <description>Visualization generated using OpenAPI Generator "java"</description>
+
+    <build>
+        <defaultGoal>spring-boot:run</defaultGoal>
+        <finalName>visualization_module</finalName>
+
+        <plugins>
+            <plugin>
+                <groupId>org.openapitools</groupId>
+                <artifactId>openapi-generator-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            <inputSpec>${project.basedir}/openapi.yaml</inputSpec>
+                            <generatorName>spring</generatorName>
+                            <apiPackage>cz.muni.pa165.generated.api</apiPackage>
+                            <modelPackage>cz.muni.pa165.generated.model</modelPackage>
+                            <configOptions>
+                                <basePackage>cz.muni.pa165</basePackage>
+                                <configPackage>cz.muni.pa165.generated.config</configPackage>
+                                <useSpringBoot3>true</useSpringBoot3>
+                                <useTags>true</useTags>
+                                <delegatePattern>true</delegatePattern>
+                            </configOptions>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <executable>true</executable>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.validation</groupId>
+            <artifactId>jakarta.validation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-models-jakarta</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations-jakarta</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openapitools</groupId>
+            <artifactId>jackson-databind-nullable</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.pdfbox</groupId>
+            <artifactId>pdfbox</artifactId>
+            <version>2.0.24</version>
+        </dependency>
+        <!--		Actuator dependency-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+
+        <!--		Prometheus dependency -->
+        <dependency>
+            <groupId>io.micrometer</groupId>
+            <artifactId>micrometer-registry-prometheus</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/visualization/src/main/java/cz/muni/pa165/facade/VisualizationFacade.java b/visualization/src/main/java/cz/muni/pa165/facade/VisualizationFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..852ba2006db9c4536539f63677ab8f374927bd42
--- /dev/null
+++ b/visualization/src/main/java/cz/muni/pa165/facade/VisualizationFacade.java
@@ -0,0 +1,25 @@
+package cz.muni.pa165.facade;
+
+import cz.muni.pa165.generated.model.CarDto;
+import cz.muni.pa165.service.VisualizationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+
+@Service
+public class VisualizationFacade {
+
+    private final VisualizationService visualizationService;
+
+    @Autowired
+    public VisualizationFacade(VisualizationService visualizationService) {
+
+        this.visualizationService = visualizationService;
+    }
+
+    public Resource generateCarPdf(CarDto carDto) throws IOException {
+        return visualizationService.generateCarPdf(carDto);
+    }
+}
diff --git a/visualization/src/main/java/cz/muni/pa165/rest/VisualizationController.java b/visualization/src/main/java/cz/muni/pa165/rest/VisualizationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..155ca6fef900ee31e730efd08456ef296f9e554f
--- /dev/null
+++ b/visualization/src/main/java/cz/muni/pa165/rest/VisualizationController.java
@@ -0,0 +1,45 @@
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.facade.VisualizationFacade;
+import cz.muni.pa165.generated.api.VisualizationApiDelegate;
+import cz.muni.pa165.generated.model.CarDto;
+import cz.muni.pa165.generated.model.GenerateCarPdfRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+@Component
+public class VisualizationController implements VisualizationApiDelegate {
+    private static final Logger log = LoggerFactory.getLogger(VisualizationController.class);
+    private final VisualizationFacade visualizationFacade;
+
+    @Autowired
+    public VisualizationController(VisualizationFacade visualizationFacade) {
+
+        this.visualizationFacade = visualizationFacade;
+    }
+
+    @Override
+    public ResponseEntity<Resource> generateCarPdf(GenerateCarPdfRequest generateCarPdfRequest) {
+        log.debug("generateCarPdf() called");
+        try {
+            var resource = visualizationFacade.generateCarPdf(generateCarPdfRequest.getCar());
+            return ResponseEntity.ok()
+                    .header(HttpHeaders.CONTENT_DISPOSITION,
+                            String.format("attachment; filename=car-%s.pdf", generateCarPdfRequest.getCar().getId()))
+                    .contentType(MediaType.APPLICATION_PDF)
+                    .contentLength(resource.contentLength())
+                    .body(resource);
+        } catch (IOException e) {
+            return new ResponseEntity<>(null, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+}
diff --git a/visualization/src/main/java/cz/muni/pa165/rest/exceptionhandling/ApiError.java b/visualization/src/main/java/cz/muni/pa165/rest/exceptionhandling/ApiError.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e1161dcad4805649ce3757c0c7983dd52f30e9c
--- /dev/null
+++ b/visualization/src/main/java/cz/muni/pa165/rest/exceptionhandling/ApiError.java
@@ -0,0 +1,62 @@
+package cz.muni.pa165.rest.exceptionhandling;
+
+import org.springframework.http.HttpStatus;
+
+import java.time.LocalDateTime;
+
+public class ApiError {
+
+    private LocalDateTime timestamp;
+    private HttpStatus status;
+    private String message;
+    private String path;
+
+    public ApiError(LocalDateTime timestamp, HttpStatus status, String message, String path) {
+        this.timestamp = timestamp;
+        this.status = status;
+        this.message = message;
+        this.path = path;
+    }
+
+    public LocalDateTime getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(LocalDateTime timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public HttpStatus getStatus() {
+        return status;
+    }
+
+    public void setStatus(HttpStatus status) {
+        this.status = status;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    @Override
+    public String toString() {
+        return "ApiError{" +
+                "timestamp=" + timestamp +
+                ", status=" + status +
+                ", message='" + message + '\'' +
+                ", path='" + path + '\'' +
+                '}';
+    }
+}
diff --git a/visualization/src/main/java/cz/muni/pa165/rest/exceptionhandling/CustomRestGlobalExceptionHandling.java b/visualization/src/main/java/cz/muni/pa165/rest/exceptionhandling/CustomRestGlobalExceptionHandling.java
new file mode 100644
index 0000000000000000000000000000000000000000..efbb519ca9bcdcff5e7eddddb8851c2b292ce19d
--- /dev/null
+++ b/visualization/src/main/java/cz/muni/pa165/rest/exceptionhandling/CustomRestGlobalExceptionHandling.java
@@ -0,0 +1,36 @@
+package cz.muni.pa165.rest.exceptionhandling;
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.util.UrlPathHelper;
+
+import java.io.IOException;
+import java.time.Clock;
+import java.time.LocalDateTime;
+
+@RestControllerAdvice
+public class CustomRestGlobalExceptionHandling {
+
+    private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper();
+
+    @ExceptionHandler({Exception.class})
+    public ResponseEntity<ApiError> handleAll(final Exception ex, HttpServletRequest request) {
+        final ApiError apiError = new ApiError(
+                LocalDateTime.now(Clock.systemUTC()),
+                HttpStatus.INTERNAL_SERVER_ERROR,
+                getInitialException(ex).getLocalizedMessage(),
+                URL_PATH_HELPER.getRequestUri(request));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    private Exception getInitialException(Exception ex) {
+        while (ex.getCause() != null) {
+            ex = (Exception) ex.getCause();
+        }
+        return ex;
+    }
+}
diff --git a/visualization/src/main/java/cz/muni/pa165/service/VisualizationService.java b/visualization/src/main/java/cz/muni/pa165/service/VisualizationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..82a959d0a2c445db9f7ef760de96692330db7a62
--- /dev/null
+++ b/visualization/src/main/java/cz/muni/pa165/service/VisualizationService.java
@@ -0,0 +1,128 @@
+package cz.muni.pa165.service;
+
+import cz.muni.pa165.generated.model.CarDto;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.core.io.Resource;
+import org.springframework.stereotype.Service;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+@Service
+public class VisualizationService {
+    public Resource generateCarPdf(CarDto carDto) throws IOException {
+        try (PDDocument document = new PDDocument()) {
+            // Add a new page to the document
+            PDPage page = new PDPage();
+            document.addPage(page);
+
+            // Create a content stream for the page
+            PDPageContentStream contentStream = new PDPageContentStream(document, page);
+
+            contentStream.setFont(PDType1Font.HELVETICA_BOLD, 16);
+
+            contentStream.beginText();
+            contentStream.newLineAtOffset(100, 700);
+            contentStream.showText("Generated car PDF");
+
+            contentStream.setFont(PDType1Font.HELVETICA, 14);
+            contentStream.newLineAtOffset(0, -20);
+            contentStream.showText("Car ID: " + carDto.getId());
+
+            contentStream.setFont(PDType1Font.HELVETICA_BOLD, 14);
+            contentStream.newLineAtOffset(0, -30);
+            contentStream.showText("Components:");
+            contentStream.newLineAtOffset(15, 0);
+
+            contentStream.setFont(PDType1Font.HELVETICA, 12);
+            if (carDto.getComponents() == null || carDto.getComponents().isEmpty()) {
+                contentStream.newLineAtOffset(0, -20);
+                contentStream.showText("No components");
+            } else {
+                for (var c: carDto.getComponents()) {
+                    contentStream.newLineAtOffset(0, -20);
+                    contentStream.showText("ID: " + c.getId());
+                    contentStream.newLineAtOffset(0, -20);
+                    contentStream.showText("Information: " + c.getInformation());
+                    contentStream.newLineAtOffset(0, -20);
+                    contentStream.showText("Type: " + c.getComponentType());
+                    contentStream.newLineAtOffset(0, -20);
+                    contentStream.showText("Weight: " + c.getWeight());
+                    contentStream.newLineAtOffset(0, -20);
+                }
+            }
+
+            contentStream.setFont(PDType1Font.HELVETICA_BOLD, 14);
+            contentStream.newLineAtOffset(-15, -30);
+            contentStream.showText("Driver:");
+            contentStream.newLineAtOffset(15, 0);
+
+            contentStream.setFont(PDType1Font.HELVETICA, 12);
+            if (carDto.getDriver() == null) {
+                contentStream.newLineAtOffset(0, -20);
+                contentStream.showText("No driver");
+            } else {
+                var d = carDto.getDriver();
+                contentStream.newLineAtOffset(0, -20);
+                contentStream.showText("ID: " + d.getId());
+                contentStream.newLineAtOffset(0, -20);
+                contentStream.showText("Name: " + d.getName());
+                contentStream.newLineAtOffset(0, -20);
+                contentStream.showText("Surname: " + d.getSurname());
+                contentStream.newLineAtOffset(0, -20);
+                contentStream.showText("Nationality: " + d.getNationality());
+                contentStream.newLineAtOffset(0, -20);
+                contentStream.showText("Birthday: " + d.getBirthday());
+                contentStream.newLineAtOffset(0, -20);
+                contentStream.showText("Height: " + d.getHeight());
+
+                contentStream.newLineAtOffset(0, -20);
+                contentStream.showText("Characteristics:");
+                contentStream.newLineAtOffset(15, 0);
+                for (var c: carDto.getDriver().getCharacteristics()) {
+                    contentStream.newLineAtOffset(0, -20);
+                    contentStream.showText("- " + c.getValue());
+                }
+            }
+
+            contentStream.endText();
+
+            // Close the content stream
+            contentStream.close();
+
+            // Write the PDF document to a byte array
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            document.save(baos);
+
+            String outputDirName = "output-data";
+            Path projectDir = Paths.get("");
+            Path outputDirPath = projectDir.resolve(outputDirName);
+
+            // Create output directory if it does not exist
+            if (!Files.exists(outputDirPath)) {
+                Files.createDirectories(outputDirPath);
+            }
+
+            // Save generated file
+            Files.write(outputDirPath.resolve(getFilename()), baos.toByteArray());
+
+            return new ByteArrayResource(baos.toByteArray());
+        }
+    }
+
+    private String getFilename() {
+        LocalDateTime currentTime = LocalDateTime.now();
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
+
+        return String.format("car-updated-%s.pdf", currentTime.format(formatter));
+    }
+}
diff --git a/visualization/src/main/resources/application.properties b/visualization/src/main/resources/application.properties
new file mode 100644
index 0000000000000000000000000000000000000000..54fbf76c7b79297e3499b7c4d940166af1d61d96
--- /dev/null
+++ b/visualization/src/main/resources/application.properties
@@ -0,0 +1,6 @@
+server.port=8082
+
+management.endpoints.web.exposure.include=health,metrics,loggers,beans,prometheus
+management.endpoint.health.show-details=always
+management.endpoint.health.show-components=always
+management.endpoint.health.probes.enabled=true
diff --git a/visualization/src/test/java/cz/muni/pa165/rest/VisualizationControllerTest.java b/visualization/src/test/java/cz/muni/pa165/rest/VisualizationControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..48900adcd521221852e772ec74cb8c82d22b91d5
--- /dev/null
+++ b/visualization/src/test/java/cz/muni/pa165/rest/VisualizationControllerTest.java
@@ -0,0 +1,35 @@
+/*
+package cz.muni.pa165.rest;
+
+import cz.muni.pa165.facade.VisualizationFacade;
+import cz.muni.pa165.generated.model.CarDto;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+*/
+/**
+ * @author Michal Badin
+ *//*
+
+class VisualizationControllerTest {
+    private static final Logger log = LoggerFactory.getLogger(VisualizationControllerTest.class);
+    private final VisualizationFacade mockFacade = Mockito.mock(VisualizationFacade.class);
+    private final VisualizationController controller = new VisualizationController(mockFacade);
+
+
+    @Test
+    void testGenerateCarPdf() throws IOException {
+        log.debug("testGenerateCarPdf() running");
+
+        var car = new CarDto().id(1L);
+
+        controller.generateCarPdf(car);
+
+        Mockito.verify(mockFacade, Mockito.times(1)).generateCarPdf(car);
+    }
+}
+*/
diff --git a/visualization/src/test/java/cz/muni/pa165/rest/VisualizationIT.java b/visualization/src/test/java/cz/muni/pa165/rest/VisualizationIT.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d73c006504baa2c5d774db05b8250ea44929f0c
--- /dev/null
+++ b/visualization/src/test/java/cz/muni/pa165/rest/VisualizationIT.java
@@ -0,0 +1,54 @@
+package cz.muni.pa165.rest;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import cz.muni.pa165.generated.model.CarDto;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.util.Map;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Integration tests. Run by "mvn verify / mvn test / mvn clean install".
+ * No need to test all the rest controllers logic here, just test that the endpoints are working*/
+
+
+@SpringBootTest
+@AutoConfigureMockMvc
+class VisualizationIT {
+    private static final Logger log = LoggerFactory.getLogger(VisualizationIT.class);
+    private final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @Test
+    void testGenerateCarPdf() throws Exception {
+        log.info("testGenerateCarPdf() running");
+
+        var carDto = new CarDto().id(1L);
+        var requestBody = mapper.writeValueAsString(Map.of("car", carDto));
+
+        String response = mockMvc.perform(
+                        post("/visualization")
+                                .accept(MediaType.APPLICATION_PDF)
+                                .contentType(MediaType.APPLICATION_JSON)
+                                .content(requestBody))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentType();
+        log.info("response: {}", response);
+
+        Assertions.assertNotNull(response);
+        Assertions.assertEquals("application/pdf", response);
+    }
+}