Repository: InspiredImpact/tense (language: python)

Issue:
Removing unnecessary async
## Migrating from async to sync. CPU-bound parser.
> Extends https://github.com/Animatea/aiotense/issues/1

This project is a string parser that is not bounds to IO and depends of the CPU. 
Therefore, in the refactored project, parsers and resolvers will be implemented through generators for maximum performance.

> Note.
> The old (*aiotense*) library will not be removed, but the documentation will be changed.

### Part of changes:

- aiotense -> tense
- accelerated by ~69 times (from `0.0010508000050322153` to `0.000015400000847876072` for 1 call)


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e69de29..dc6cbeb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -0,0 +1,16 @@
+# Tense 1.0.0b (22.06.2022)
+## Breaking Changes
+- `aiotense` -> `tense`
+- changed tests
+- changed examples
+
+## Features
+- added simple benchmarks (benchmarks.py)
+- added noxfile for multiversion tests
+- resolvers now work like iterators
+- units of time now is hashable dataclasses
+- added `iteration_speedup` parameter to TenseParser factory
+
+## Customization
+- UML diagram removed
+- All README files changed
diff --git a/PYPI_README.md b/PYPI_README.md
index 81a43ea..796b062 100644
--- a/PYPI_README.md
+++ b/PYPI_README.md
@@ -1,5 +1,5 @@
 <div id="top"></div>
-Project: aiotense
+Project: tense
 <br>
 License: Apache 2.0
 <br>
@@ -15,17 +15,14 @@ Topic: Utilities
 <br />
     <p align="center">
     <br />
-    <a href="https://animatea.github.io/aiotense/">Documentation</a>
+    <a href="https://animatea.github.io/tense/">Documentation</a>
     ·
-    <a href="https://github.com/Animatea/aiotense/issues">Report Bug</a>
+    <a href="https://github.com/Animatea/tense/issues">Report Bug</a>
     ·
-    <a href="https://github.com/Animatea/aiotense/issues">Request Feature</a>
+    <a href="https://github.com/Animatea/tense/issues">Request Feature</a>
     </p>
 <div id="top"></div>
-<p align="center">
-   <a href="i18n/ua_README.md"><img height="20" src="https://img.shields.io/badge/language-ua-green?style=social&logo=googletranslate"></a>
-   <a href="i18n/ru_README.md"><img height="20" src="https://img.shields.io/badge/language-ru-green?style=social&logo=googletranslate"></a>
-</p>
+
 <details>
   <summary>Table of Contents</summary>
   <ol>
@@ -38,7 +35,7 @@ Topic: Utilities
     <li>
       <a href="#getting-started">Getting Started</a>
       <ul>
-        <li><a href="#with-pypi">With PyPi</a></li>
+        <li><a href="#with-pypi">With Pip</a></li>
         <li><a href="#with-poetry">With Poetry</a></li>
       </ul>
     </li>
@@ -60,8 +57,7 @@ Topic: Utilities
 </details>
 
 ## About The Project
-<a href="https://circleci.com/gh/Animatea/aiotense/tree/main"><img height="20" src="https://circleci.com/gh/Animatea/aiotense/tree/main.svg?style=svg&circle-token=066eb74fc70db6eeaa6df5ade9e3d2df131b1de1"></a>
-<a href="https://pypi.org/project/aiotense/"><img height="20" alt="PyPi" src="https://img.shields.io/pypi/v/aiotense"></a>
+<a href="https://circleci.com/gh/Animatea/tense/tree/main"><img height="20" src="https://dl.circleci.com/status-badge/img/gh/Animatea/tense/tree/main.svg?style=svg"></a>
 <a href="https://pypi.org/project/mypy/"><img height="20" alt="Mypy badge" src="http://www.mypy-lang.org/static/mypy_badge.svg"></a>
 <a href="https://github.com/psf/black"><img height="20" alt="Black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
 <a href="https://pycqa.github.io/isort/"><img height="20" alt="Supported python versions" src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336">
@@ -77,33 +73,31 @@ No? Then advise us to your friends :) And if you really need our tool - let's mo
 ## Getting started
 ### With PyPi
 ```bash
-$ pip3 install aiotense
+$ pip3 install tense
 ```
 
 ### With Poetry
 ```bash
-$ poetry add aiotense
+$ poetry add tense
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
 ## Usage
 ### Built-in basic
+
 ```py
-import asyncio
 import datetime
-from aiotense import TenseParser
+from tense import TenseParser
 
 time_string = "1d2minutes 5 sec"
 
 # <-- Digit parser -->
 digit_parser = TenseParser(TenseParser.DIGIT)
-digit_value = asyncio.run(digit_parser.parse(time_string))
-# <-- Assertions -->
-assert digit_value == 86525
+assert digit_parser.parse(time_string) == 86525
 
 # <-- Timedelta parser -->
 delta_parser = TenseParser(TenseParser.TIMEDELTA)
-delta_value = asyncio.run(delta_parser.parse(time_string))
+delta_value = delta_parser.parse(time_string)
 # <-- Assertions -->
 assert isinstance(delta_value, datetime.timedelta)
 assert str(delta_value) == "1 day, 0:02:05"
@@ -111,9 +105,9 @@ assert str(delta_value) == "1 day, 0:02:05"
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
 ### Reconfiguring existing settings
+
 ```py
-import asyncio
-from aiotense import TenseParser, from_tense_file_source
+from tense import TenseParser, from_tense_file_source
 
 config_emulation = """
 [model.Tense]
@@ -127,18 +121,17 @@ aliases = my_minute, my_minutes, my_min, my_mins
 """
 parser = TenseParser(
     TenseParser.TIMEDELTA,
-    config=from_tense_file_source(config_emulation),
+    tenses=from_tense_file_source(config_emulation),
 )
-delta_value = asyncio.run(parser.parse("1 my_min 10my_mins 9  my_minutes"))
-# <-- Assertions -->
-assert str(delta_value) ==  "1:20:00" # (each 120 * 2)
+delta_value = parser.parse("1 my_min 10my_mins 9  my_minutes")
+assert str(delta_value) == "1:20:00"  # (each 120 * 2)
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
 ### Adding new settings
+
 ```py
-import asyncio
-from aiotense import TenseParser, from_tense_file_source
+from tense import TenseParser, from_tense_file_source
 
 config_emulation = """
 [model.Tense]  # This header is required.
@@ -150,10 +143,9 @@ aliases = decade, dec, decs, decades
 
 parser = TenseParser(
     TenseParser.TIMEDELTA,
-    config=from_tense_file_source(config_emulation),
+    tenses=from_tense_file_source(config_emulation),
 )
-delta_value = asyncio.run(parser.parse("1year 10 decades5   seconds"))
-# <-- Assertions -->
+delta_value = parser.parse("1year 10 decades5   seconds")
 assert str(delta_value) == "36865 days, 0:00:05"
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
@@ -161,56 +153,56 @@ assert str(delta_value) == "36865 days, 0:00:05"
 ### FAQ
 But what if you need to parse a string like: "1day and 10 minutes + 5 seconds"?
 Let's see:
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser
+>> > from tense import TenseParser
 
->>> complex_string = "1day and 10 minutes + 5 seconds"
+>> > complex_string = "1day and 10 minutes + 5 seconds"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA)
->>> asyncio.run(parser.parse(complex_string))
+>> > parser = TenseParser(TenseParser.TIMEDELTA)
+>> > parser.parse(complex_string)
 '0:00:05'
 ```
 Wait... What? 5 second? But there are days and minutes...
-- It's okay, you're using flexible aiotense! This problem is solved in two ways:
+- It's okay, you're using flexible tense! This problem is solved in two ways:
   1) You write your own time_resolver and pass it
-  2) Choose an existing one from aiotense.resolvers
+  2) Choose an existing one from tense.resolvers
 
 Let's demonstrate!
-I will use the second option, since the built-in time resolvers in aiotense are suitable for me.
+I will use the second option, since the built-in time resolvers in tense are suitable for me.
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, resolvers
+>> > from tense import TenseParser, resolvers
 
->>> complex_string = "1day and 10 minutes + 5 seconds"
+>> > complex_string = "1day and 10 minutes + 5 seconds"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
->>> asyncio.run(parser.parse(complex_string)) 
+>> > parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
+>> > parser.parse(complex_string)
 '1 day, 0:10:05'
 ```
 Well, that's better!
 
-**aiotense.application.resolvers.smart_resolver()** is also case insensitive!
+**tense.application.resolvers.smart_resolver()** is also case insensitive!
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, resolvers
+>> > from tense import TenseParser, resolvers
 
->>> complex_string = "1DAY and 10 MINUTES + 5 SECONDS"
+>> > complex_string = "1DAY and 10 MINUTES + 5 SECONDS"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
->>> asyncio.run(parser.parse(complex_string)) 
+>> > parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
+>> > parser.parse(complex_string)
 '1 day, 0:10:05'
 ```
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
 ## Examples.
-If you think that this is where the possibilities of aiotense ends, then you are wrong! 
-The possibilities of aiotense are too many for a README, so I suggest you move on to viewing 
+If you think that this is where the possibilities of tense ends, then you are wrong! 
+The possibilities of tense are too many for a README, so I suggest you move on to viewing 
 the usage examples here:
 <p align="center">
 <br />
-<a href="https://github.com/Animatea/aiotense/tree/main/examples">Aiotense Examples</a>
+<a href="https://github.com/Animatea/tense/tree/main/examples">Tense Examples</a>
 </p>
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
@@ -231,7 +223,7 @@ Don't forget to give the project a star! Thanks again!
 <!-- LICENSE -->
 ## License
 
-Distributed under the Apache 2.0 License. See [`LICENSE`](https://github.com/Animatea/aiotense/blob/main/LICENSE) for more information.
+Distributed under the Apache 2.0 License. See [`LICENSE`](https://github.com/Animatea/tense/blob/main/LICENSE) for more information.
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
diff --git a/README.md b/README.md
index b9c90dd..e865453 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 <div id="top"></div>
 <img src="assets/tense-logo.jpg" align="left" width="200px"/>
-Project: aiotense
+Project: tense
 <br>
 License: Apache 2.0
 <br>
@@ -16,11 +16,11 @@ Topic: Utilities
 <br />
     <p align="center">
     <br />
-    <a href="https://animatea.github.io/aiotense/">Documentation</a>
+    <a href="https://animatea.github.io/tense/">Documentation</a>
     ·
-    <a href="https://github.com/Animatea/aiotense/issues">Report Bug</a>
+    <a href="https://github.com/Animatea/tense/issues">Report Bug</a>
     ·
-    <a href="https://github.com/Animatea/aiotense/issues">Request Feature</a>
+    <a href="https://github.com/Animatea/tense/issues">Request Feature</a>
     </p>
 <div id="top"></div>
 <p align="center">
@@ -39,7 +39,7 @@ Topic: Utilities
     <li>
       <a href="#getting-started">Getting Started</a>
       <ul>
-        <li><a href="#with-pypi">With PyPi</a></li>
+        <li><a href="#with-pypi">With Pip</a></li>
         <li><a href="#with-poetry">With Poetry</a></li>
       </ul>
     </li>
@@ -57,56 +57,53 @@ Topic: Utilities
     <li><a href="#license">License</a></li>
     <li><a href="#contact">Contact</a></li>
     <li><a href="#acknowledgments">Acknowledgments</a></li>
+    <li><a href="#history">Project history</a></li>
   </ol>
 </details>
 
 ## About The Project
-<a href="https://circleci.com/gh/Animatea/aiotense/tree/main"><img height="20" src="https://circleci.com/gh/Animatea/aiotense/tree/main.svg?style=svg&circle-token=066eb74fc70db6eeaa6df5ade9e3d2df131b1de1"></a>
-<a href="https://pypi.org/project/aiotense/"><img height="20" alt="PyPi" src="https://img.shields.io/pypi/v/aiotense"></a>
+<a href="https://circleci.com/gh/Animatea/tense/tree/main"><img height="20" src="https://dl.circleci.com/status-badge/img/gh/Animatea/tense/tree/main.svg?style=svg"></a>
+<a href="https://pypi.org/project/tense/"><img height="20" alt="PyPi" src="https://img.shields.io/pypi/v/tense"></a>
 <a href="https://pypi.org/project/mypy/"><img height="20" alt="Mypy badge" src="http://www.mypy-lang.org/static/mypy_badge.svg"></a>
 <a href="https://github.com/psf/black"><img height="20" alt="Black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
-<a href="https://pycqa.github.io/isort/"><img height="20" alt="Supported python versions" src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336">
+<a href="https://pycqa.github.io/isort/"><img height="20" alt="Supported python versions" src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336"></a>
 
 ### Welcome
 > Have you ever needed to convert, for example, the string "1d1minute 2 sec" 
 to the number of seconds or a datetime.timedelta object?
 
 No? Then advise us to your friends :) And if you really need our tool - let's move on!
-<h5 align="center">UML Diagram with the main logic of the project</h5>
-<img src="assets/tense-uml.jpg" align="left"/>
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
 ## Getting started
 ### With PyPi
 ```bash
-$ pip3 install aiotense
+$ pip3 install tense
 ```
 
 ### With Poetry
 ```bash
-$ poetry add aiotense
+$ poetry add tense
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
 ## Usage
 ### Built-in basic
+
 ```py
-import asyncio
 import datetime
-from aiotense import TenseParser
+from tense import TenseParser
 
 time_string = "1d2minutes 5 sec"
 
 # <-- Digit parser -->
 digit_parser = TenseParser(TenseParser.DIGIT)
-digit_value = asyncio.run(digit_parser.parse(time_string))
-# <-- Assertions -->
-assert digit_value == 86525
+assert digit_parser.parse(time_string) == 86525
 
 # <-- Timedelta parser -->
 delta_parser = TenseParser(TenseParser.TIMEDELTA)
-delta_value = asyncio.run(delta_parser.parse(time_string))
+delta_value = delta_parser.parse(time_string)
 # <-- Assertions -->
 assert isinstance(delta_value, datetime.timedelta)
 assert str(delta_value) == "1 day, 0:02:05"
@@ -114,9 +111,9 @@ assert str(delta_value) == "1 day, 0:02:05"
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
 ### Reconfiguring existing settings
+
 ```py
-import asyncio
-from aiotense import TenseParser, from_tense_file_source
+from tense import TenseParser, from_tense_file_source
 
 config_emulation = """
 [model.Tense]
@@ -130,18 +127,17 @@ aliases = my_minute, my_minutes, my_min, my_mins
 """
 parser = TenseParser(
     TenseParser.TIMEDELTA,
-    config=from_tense_file_source(config_emulation),
+    tenses=from_tense_file_source(config_emulation),
 )
-delta_value = asyncio.run(parser.parse("1 my_min 10my_mins 9  my_minutes"))
-# <-- Assertions -->
-assert str(delta_value) ==  "1:20:00" # (each 120 * 2)
+delta_value = parser.parse("1 my_min 10my_mins 9  my_minutes")
+assert str(delta_value) == "1:20:00"  # (each 120 * 2)
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
 ### Adding new settings
+
 ```py
-import asyncio
-from aiotense import TenseParser, from_tense_file_source
+from tense import TenseParser, from_tense_file_source
 
 config_emulation = """
 [model.Tense]  # This header is required.
@@ -153,10 +149,9 @@ aliases = decade, dec, decs, decades
 
 parser = TenseParser(
     TenseParser.TIMEDELTA,
-    config=from_tense_file_source(config_emulation),
+    tenses=from_tense_file_source(config_emulation),
 )
-delta_value = asyncio.run(parser.parse("1year 10 decades5   seconds"))
-# <-- Assertions -->
+delta_value = parser.parse("1year 10 decades5   seconds")
 assert str(delta_value) == "36865 days, 0:00:05"
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
@@ -164,56 +159,56 @@ assert str(delta_value) == "36865 days, 0:00:05"
 ### FAQ
 But what if you need to parse a string like: "1day and 10 minutes + 5 seconds"?
 Let's see:
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser
+>> > from tense import TenseParser
 
->>> complex_string = "1day and 10 minutes + 5 seconds"
+>> > complex_string = "1day and 10 minutes + 5 seconds"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA)
->>> asyncio.run(parser.parse(complex_string))
+>> > parser = TenseParser(TenseParser.TIMEDELTA)
+>> > parser.parse(complex_string)
 '0:00:05'
 ```
 Wait... What? 5 second? But there are days and minutes...
-- It's okay, you're using flexible aiotense! This problem is solved in two ways:
+- It's okay, you're using flexible tense! This problem is solved in two ways:
   1) You write your own time_resolver and pass it
-  2) Choose an existing one from aiotense.resolvers
+  2) Choose an existing one from tense.resolvers
 
 Let's demonstrate!
-I will use the second option, since the built-in time resolvers in aiotense are suitable for me.
+I will use the second option, since the built-in time resolvers in tense are suitable for me.
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, resolvers
+>> > from tense import TenseParser, resolvers
 
->>> complex_string = "1day and 10 minutes + 5 seconds"
+>> > complex_string = "1day and 10 minutes + 5 seconds"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
->>> asyncio.run(parser.parse(complex_string)) 
+>> > parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
+>> > parser.parse(complex_string)
 '1 day, 0:10:05'
 ```
 Well, that's better!
 
-**aiotense.application.resolvers.smart_resolver()** is also case insensitive!
+**tense.application.resolvers.smart_resolver()** is also case insensitive!
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, resolvers
+>> > from tense import TenseParser, resolvers
 
->>> complex_string = "1DAY and 10 MINUTES + 5 SECONDS"
+>> > complex_string = "1DAY and 10 MINUTES + 5 SECONDS"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
->>> asyncio.run(parser.parse(complex_string)) 
+>> > parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
+>> > parser.parse(complex_string)
 '1 day, 0:10:05'
 ```
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
 ## Examples.
-If you think that this is where the possibilities of aiotense ends, then you are wrong! 
-The possibilities of aiotense are too many for a README, so I suggest you move on to viewing 
+If you think that this is where the possibilities of tense ends, then you are wrong! 
+The possibilities of tense are too many for a README, so I suggest you move on to viewing 
 the usage examples here:
 <p align="center">
 <br />
-<a href="https://github.com/Animatea/aiotense/tree/main/examples">Aiotense Examples</a>
+<a href="https://github.com/Animatea/tense/tree/main/examples">Tense Examples</a>
 </p>
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
@@ -234,7 +229,7 @@ Don't forget to give the project a star! Thanks again!
 <!-- LICENSE -->
 ## License
 
-Distributed under the Apache 2.0 License. See [`LICENSE`](https://github.com/Animatea/aiotense/blob/main/LICENSE) for more information.
+Distributed under the Apache 2.0 License. See [`LICENSE`](https://github.com/Animatea/tense/blob/main/LICENSE) for more information.
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
 
@@ -258,3 +253,17 @@ Distributed under the Apache 2.0 License. See [`LICENSE`](https://github.com/Ani
 * [MkDocs Material](https://squidfunk.github.io/mkdocs-material/)
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
+
+## History
+Initially, the project was made asynchronous, which significantly slowed down the parsing process, because tense is a CPU-bound module.
+
+By refactoring the project, we got a `~x22.31` speedup in processing complex strings (using `smart_resolver`).
+> Was: `~0.00095030...`μs | Now: `~0.00004260...`μs
+
+And `~x38.28` speed up processing simple strings (using `basic_resolver`).
+> Was: `~0.00062400...`μs | Now: `~0.00001630...`μs
+
+The previous (async) version is still available in the repository branches - https://github.com/Animatea/tense/tree/async-final,
+but the site with the documentation will be changed.
+
+<p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/back_to-top-green?style=social&logo=github"></a></p>
diff --git a/TODO.md b/TODO.md
index ebe24f8..bdf4a81 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,1 +1,3 @@
-- [ ] Test coverage [~30/100%]
+# Tense (1.0.0b) (22.06.22)
+- [ ] Remake docs for `tense`
+- [ ] Add github templates
diff --git a/aiotense/application/factory.py b/aiotense/application/factory.py
deleted file mode 100644
index e998847..0000000
--- a/aiotense/application/factory.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# Copyright 2022 Animatea
-#
-# Licensed 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
-#
-#     http://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.
-"""Aiotense factory."""
-from __future__ import annotations
-
-__all__ = ["TenseParser"]
-
-from typing import TYPE_CHECKING, Any, Callable, Optional, Type
-
-from aiotense.adapters import parsers, repository
-from aiotense.domain import model
-
-if TYPE_CHECKING:
-    from aiotense.application.ports import converters
-
-from . import exceptions, resolvers
-from .ports import parsers as abc_parsers
-
-_tenses = repository.TenseRepository()
-
-
-class TenseParser:
-    """Base parsers factory.
-
-    Parameters:
-    -----------
-    parser_cls: :class:`Any` = DIGIT
-        Concrete parser type.
-    tense: :class:`model.Tense` = model.Tense.from_dict(_tenses.config), *
-        Configuration for concrete parser.
-
-    Raises:
-    -------
-    :class:`exceptions.InvalidParserType`
-        Raises if 'parser_cls' is not subclass of :class:`abc_parsers.AbstractParser`
-    """
-
-    TIMEDELTA = parsers.TimedeltaParser
-    DIGIT = parsers.DigitParser
-
-    def __new__(
-        cls,
-        parser_cls: Type[abc_parsers.AbstractParser] = DIGIT,
-        *,
-        config: Optional[dict[str, Any]] = None,
-        converter: Optional[converters.AbstractConverter] = None,
-        time_resolver: Optional[Callable[[str, model.Tense], list[str]]] = None,
-    ) -> abc_parsers.AbstractParser:
-        if config is not None:
-            _tenses._config.update(config)
-
-        if time_resolver is None:
-            time_resolver = resolvers.basic_resolver
-
-        if not issubclass(parser_cls, abc_parsers.AbstractParser):
-            raise exceptions.InvalidParserType(
-                f"Invalid parser type, you can only use {parsers.__all__}."
-            )
-        instance = parser_cls.__new__(parser_cls)  # type: ignore[call-overload]
-        instance.__init__(
-            tense=model.Tense.from_dict(_tenses.config),
-            resolver=time_resolver,
-            converter=converter,
-        )
-        return instance
diff --git a/aiotense/application/ports/parsers.py b/aiotense/application/ports/parsers.py
deleted file mode 100644
index 4c6efa6..0000000
--- a/aiotense/application/ports/parsers.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright 2022 Animatea
-#
-# Licensed 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
-#
-#     http://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.
-"""Interfaces that are implemented in aiotense.adapters."""
-from __future__ import annotations
-
-__all__ = ["AbstractParser"]
-
-import abc
-from typing import TYPE_CHECKING, Any, Callable, Optional, final
-
-from aiotense.application.ports import converters
-
-if TYPE_CHECKING:
-    from aiotense.domain import model
-
-
-class AbstractParser(abc.ABC):
-    def __init__(
-        self,
-        *,
-        tense: model.Tense,
-        resolver: Optional[Callable[[str, model.Tense], list[str]]] = None,
-        converter: Optional[converters.AbstractConverter] = None,
-    ) -> None:
-        self.tense = tense
-        self.converter = converter
-        self._resolver = resolver
-
-    @final
-    async def parse(self, raw_str: str) -> Any:
-        value = await self._parse(raw_str)
-        if self.converter is not None:
-            value = await self.converter.convert(value)
-
-        return value
-
-    @abc.abstractmethod
-    def _parse(self, raw_str: str) -> Any:
-        ...
-
-    @property
-    def resolver(self) -> Optional[Callable[[str, model.Tense], list[str]]]:
-        return self._resolver
-
-    @resolver.setter
-    def resolver(self, new_resolver: Callable[[str, model.Tense], list[str]]) -> None:
-        if not callable(new_resolver):
-            raise ValueError("Resolver must be callable.")
-        self._resolver = new_resolver
diff --git a/aiotense/application/ports/repository.py b/aiotense/application/ports/repository.py
deleted file mode 100644
index 606c448..0000000
--- a/aiotense/application/ports/repository.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# Copyright 2022 Animatea
-#
-# Licensed 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
-#
-#     http://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.
-"""Interfaces that are implemented in aiotense.adapters."""
-from __future__ import annotations
-
-__all__ = ["AbstractTenseRepository"]
-
-import abc
-from typing import TYPE_CHECKING, Any, Optional, Type, TypedDict, cast
-
-if TYPE_CHECKING:
-    from aiotense.domain import units
-
-    class _VirtualUnitDict(TypedDict):
-        duration: int
-        aliases: list[str]
-
-
-class AbstractTenseRepository(abc.ABC):
-    """`Add` operations allows fluent-style."""
-
-    def __init__(self, config: Optional[dict[str, Any]] = None, /) -> None:
-        self._config = config
-
-    def __getitem__(self, item: Any) -> Any:
-        if not isinstance(item, str):
-            return NotImplemented
-        config = cast(dict[str, Any], self._config)
-        return config[item]
-
-    def __setitem__(self, key: Any, value: Any) -> Any:
-        if not isinstance(key, str) or not isinstance(value, str):
-            return NotImplemented
-        if self._config is None:
-            return NotImplemented
-        if key not in self._config:
-            raise KeyError(
-                f"Key {key} does not exist in config. New keys cannot be added."
-            )
-        self._config[key] = value
-
-    @property
-    def config(self) -> Optional[dict[str, Any]]:
-        return self._config
-
-    @abc.abstractmethod
-    def get_config(self) -> dict[str, Any]:
-        """Returns deepcopy of current config."""
-        ...
-
-    @abc.abstractmethod
-    def get_setting(self, path: str, setting: str, /) -> Any:
-        ...
-
-    @abc.abstractmethod
-    def add_virtual_unit(self, unit: units.VirtualUnit) -> AbstractTenseRepository:
-        """Adds custom unit of time. Allows fluent-style."""
-        ...
-
-    @abc.abstractmethod
-    def add_virtual_unit_dict(
-        self, unit_dict: _VirtualUnitDict
-    ) -> AbstractTenseRepository:
-        """Adds custom unit of time. Allows fluent-style."""
-        ...
-
-    @abc.abstractmethod
-    def add_aliases_to(
-        self, unit: Type[units.Unit], aliases: list[str]
-    ) -> AbstractTenseRepository:
-        """Adds aliases to unit of time. Allows fluent-style."""
-        ...
diff --git a/aiotense/application/resolvers.py b/aiotense/application/resolvers.py
deleted file mode 100644
index 805e9f4..0000000
--- a/aiotense/application/resolvers.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright 2022 Animatea
-#
-# Licensed 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
-#
-#     http://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.
-"""Time resolvers."""
-from __future__ import annotations
-
-__all__ = ["basic_resolver", "smart_resolver"]
-
-import re
-from typing import TYPE_CHECKING
-
-if TYPE_CHECKING:
-    from aiotense.domain import model
-
-DIGIT_PATTERN: re.Pattern[str] = re.compile(r"(\d+)")
-
-
-def basic_resolver(raw_str: str, _: model.Tense) -> list[str]:
-    """Example of supported patterns:
-    * '1d1min'
-    * '1d 1min'
-    * '1d1min 2 seconds'
-    * etc
-    """
-    return list(
-        filter(
-            bool,
-            DIGIT_PATTERN.split(raw_str.replace(" ", "")),
-        )
-    )
-
-
-def smart_resolver(raw_str: str, tense: model.Tense) -> list[str]:
-    """Example of supported patterns:
-    * All patterns from `resolve_time_string`
-    * 1year and 10 months + 5 seconds
-    * etc
-    """
-    basic_resolve = basic_resolver(raw_str, tense)
-    if any(not p.isalpha() for p in basic_resolve if not p.isdigit()):
-        # Removes any char of string.punctuation
-        def _resolve_p(p: str, /) -> str:
-            return p if p.isdigit() else "".join(filter(str.isalpha, p))
-
-        basic_resolve = [_resolve_p(p) for p in basic_resolve[:]]
-
-    unit_aliases: list[str] = sum((u.aliases for u in tense), [])
-    for idx, part in enumerate(basic_resolve):
-        if part.isdigit():
-            continue
-
-        part = part.lower()  # case insensitive
-        for alias in unit_aliases:
-            if alias in part:
-                if len(alias) == 1 and len(part) > 1:
-                    # For 'd', 's', 'y', ... aliases.
-                    continue
-                basic_resolve.pop(idx)
-                basic_resolve.insert(idx, alias)
-
-    return basic_resolve
diff --git a/aiotense/domain/model.py b/aiotense/domain/model.py
deleted file mode 100644
index 117f82e..0000000
--- a/aiotense/domain/model.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2022 Animatea
-#
-# Licensed 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
-#
-#     http://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.
-"""Tense domain."""
-from __future__ import annotations
-
-__all__ = ["Tense"]
-
-import warnings
-from dataclasses import dataclass, field
-from typing import Any, Iterator
-
-from aiotense.domain import units
-
-
-@dataclass
-class Tense:
-    second: units.Second
-    minute: units.Minute
-    hour: units.Hour
-    day: units.Day
-    week: units.Week
-    year: units.Year
-    multiplier: int = 1
-    virtual: list[dict[str, Any]] = field(default_factory=list)
-
-    def __post_init__(self) -> None:
-        self._cached_units: list[units.Unit] = []
-        if self.multiplier <= 0:
-            warnings.warn(
-                "The time multiplier is less than zero, the work of "
-                "parsers may be incorrect. It is recommended to set "
-                "the value more than zero."
-            )
-        if self.virtual:
-            self._resolve_virtual()
-
-    def __iter__(self) -> Iterator[units.Unit]:
-        _cached_units = self._cached_units
-        if _cached_units:
-            yield from _cached_units
-        else:
-            for unit in self.__dict__.values():
-                if isinstance(unit, units.Unit):
-                    self._cached_units.append(unit)
-                    yield unit
-
-    def _resolve_virtual(self) -> None:
-        for n, unit_dict in enumerate(self.virtual):
-            self.__dict__[f"virtual{n}"] = units.VirtualUnit(**unit_dict)
-
-    @classmethod
-    def from_dict(cls, tense_dict: dict[str, Any], /) -> Tense:
-        tense_attrs = {}
-        for key, attrs in tense_dict.items():
-            module, cls_name = key.split(".")
-            if cls_name == cls.__name__:
-                if not isinstance(attrs, dict):
-                    continue
-
-                tense_attrs.update(attrs)
-                continue
-
-            module = globals()[module]
-            unit_cls = getattr(module, cls_name)
-            tense_attrs[cls_name.lower()] = unit_cls(**attrs)
-            continue
-
-        return cls(**tense_attrs)
diff --git a/aiotense/domain/units.py b/aiotense/domain/units.py
deleted file mode 100644
index 5957881..0000000
--- a/aiotense/domain/units.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright 2022 Animatea
-#
-# Licensed 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
-#
-#     http://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.
-"""Tense domain."""
-from __future__ import annotations
-
-__all__ = ["Unit", "Minute", "Hour", "Day", "Week", "VirtualUnit", "Second"]
-
-import warnings
-from dataclasses import dataclass
-
-
-@dataclass
-class Unit:
-    aliases: list[str]
-    duration: int
-
-    def __post_init__(self) -> None:
-        if self.duration <= 0:
-            warnings.warn(
-                "The unit duration is less than zero, the work of "
-                "parsers may be incorrect. It is recommended to set "
-                "the value more than zero."
-            )
-
-
-@dataclass
-class VirtualUnit(Unit):
-    """Used to create custom units of time."""
-
-    pass
-
-
-@dataclass
-class Second(Unit):
-    duration: int = 1
-
-
-@dataclass
-class Minute(Unit):
-    duration: int = 60
-
-
-@dataclass
-class Hour(Unit):
-    duration: int = 60 * 60
-
-
-@dataclass
-class Day(Unit):
-    duration: int = 60 * 60 * 24
-
-
-@dataclass
-class Week(Unit):
-    duration: int = 60 * 60 * 24 * 7
-
-
-@dataclass
-class Year(Unit):
-    duration: int = 60 * 60 * 24 * 365
diff --git a/aiotense/service_layer/functional.py b/aiotense/service_layer/functional.py
deleted file mode 100644
index 1894283..0000000
--- a/aiotense/service_layer/functional.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2022 Animatea
-#
-# Licensed 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
-#
-#     http://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.
-"""Functional project tools."""
-__all__ = ["async_lru"]
-
-import functools
-from collections import OrderedDict
-from typing import Any, Awaitable, Callable, Coroutine, ParamSpec, TypeVar, cast
-
-P = ParamSpec("P")
-RT = TypeVar("RT")
-
-
-def async_lru(
-    maxsize: int = 128,
-) -> Callable[[Callable[P, Awaitable[RT]]], Callable[P, Awaitable[RT]]]:
-    """LRU cache wrapper for coroutines.
-    A simple LRU cache that based on collections.OrderedDict where the key is the
-        ~ function name + (positional arguments) + {keyword arguments}
-
-    Parameters:
-    -----------
-    maxsize: :class:`int` = 128
-        Cache size.
-    """
-    cache: OrderedDict[str, Any] = OrderedDict()
-
-    def inner(
-        fn: Callable[P, Awaitable[RT]],
-    ) -> Callable[P, Coroutine[Any, Any, RT]]:
-        @functools.wraps(fn)
-        async def wrapper(*args: P.args, **kwargs: P.kwargs) -> RT:
-            key = getattr(fn, "__name__") + str((args, kwargs))
-            if key not in cache:
-                if len(cache) >= maxsize:
-                    cache.popitem(last=False)
-
-                cache[key] = await fn(*args, **kwargs)
-            return cast(RT, cache[key])
-
-        return wrapper
-
-    return inner
diff --git a/assets/tense-uml.jpg b/assets/tense-uml.jpg
deleted file mode 100644
index 0ae7067..0000000
Binary files a/assets/tense-uml.jpg and /dev/null differ
diff --git a/benchmarks.py b/benchmarks.py
new file mode 100644
index 0000000..19bacf4
--- /dev/null
+++ b/benchmarks.py
@@ -0,0 +1,105 @@
+from __future__ import annotations
+
+import copy
+import inspect
+import logging
+import timeit
+from typing import TYPE_CHECKING, Callable, Final, TypeVar
+
+from tense import TenseParser, model, resolvers
+from tense.adapters import repository
+
+if TYPE_CHECKING:
+    _BT = TypeVar("_BT", bound=Callable[..., None])  # Benchmark Type
+
+logging.basicConfig()
+_BENCHMARK: logging.Logger = logging.getLogger(__file__)
+_BENCHMARK.setLevel(logging.INFO)
+
+_BASIC_UNRESOLVED_STR1: Final[str] = "1 day 2 seconds"
+_BASIC_UNRESOLVED_STR2: Final[str] = "1day 2seconds"
+
+_COMPLEX_UNRESOLVED_STR: Final[str] = "1day and 2minutes + 5 seconds"
+
+_TENSE = model.Tense.from_repository(repository.TenseRepository())
+_BASIC_TENSE_DIGIT_PARSER = TenseParser(TenseParser.DIGIT, iteration_speedup=True)
+_SMART_TENSE_DIGIT_PARSER = TenseParser(
+    TenseParser.DIGIT,
+    iteration_speedup=True,
+    time_resolver=resolvers.smart_resolver,
+)
+
+
+def benchmark(*, number: int) -> Callable[[_BT], _BT]:
+    def _code(_str: str) -> str:  # type: ignore[return]
+        for substr in (lst := _str.split("\n")):
+            if "def" not in substr:
+                continue
+            return "\n".join(
+                map(
+                    str.strip,
+                    lst[lst.index(substr) + 1 :],
+                )
+            )
+
+    def inner(fn: _BT) -> _BT:
+        _BENCHMARK.info(f"Running benchmark for {fn.__name__} {number:,} times.")
+        _time = timeit.timeit(
+            stmt=_code(inspect.getsource(fn)),
+            number=number,
+            globals=copy.copy(globals()),
+        )
+        _BENCHMARK.info(f"The benchmark ({number:,} times) for foo took {_time:.8f}µs.")
+        return fn
+
+    return inner
+
+
+@benchmark(number=1)
+def basic_resolver_one_time1() -> None:
+    resolvers.basic_resolver(_BASIC_UNRESOLVED_STR1, _TENSE)
+
+
+@benchmark(number=1)
+def basic_resolver_one_time2() -> None:
+    resolvers.basic_resolver(_BASIC_UNRESOLVED_STR2, _TENSE)
+
+
+@benchmark(number=1_000_000)
+def basic_resolver_1million_times1() -> None:
+    resolvers.basic_resolver(_BASIC_UNRESOLVED_STR1, _TENSE)
+
+
+@benchmark(number=1_000_000)
+def basic_resolver_1million_times2() -> None:
+    resolvers.basic_resolver(_BASIC_UNRESOLVED_STR2, _TENSE)
+
+
+@benchmark(number=1)
+def smart_resolver_one_time() -> None:
+    resolvers.smart_resolver(_COMPLEX_UNRESOLVED_STR, _TENSE)
+
+
+@benchmark(number=1_000_000)
+def smart_resolver_1million_times() -> None:
+    resolvers.smart_resolver(_COMPLEX_UNRESOLVED_STR, _TENSE)
+
+
+@benchmark(number=1)
+def basic_tense_parser_one_time() -> None:
+    _BASIC_TENSE_DIGIT_PARSER.parse(_BASIC_UNRESOLVED_STR1)
+
+
+@benchmark(number=1_000_000)
+def basic_tense_parser_1million_times() -> None:
+    _BASIC_TENSE_DIGIT_PARSER.parse(_BASIC_UNRESOLVED_STR1)
+
+
+@benchmark(number=1)
+def smart_tense_parser_one_time() -> None:
+    _SMART_TENSE_DIGIT_PARSER.parse(_COMPLEX_UNRESOLVED_STR)
+
+
+@benchmark(number=1_000_000)
+def smart_tense_parser_1million_times() -> None:
+    _SMART_TENSE_DIGIT_PARSER.parse(_COMPLEX_UNRESOLVED_STR)
diff --git a/docs/docs/doc_assets/language.png b/docs/docs/doc_assets/language.png
deleted file mode 100644
index 0e40c3a..0000000
Binary files a/docs/docs/doc_assets/language.png and /dev/null differ
diff --git a/docs/docs/doc_assets/oops.jpg b/docs/docs/doc_assets/oops.jpg
deleted file mode 100644
index 8cf1fea..0000000
Binary files a/docs/docs/doc_assets/oops.jpg and /dev/null differ
diff --git a/docs/docs/doc_assets/tense-uml.jpg b/docs/docs/doc_assets/tense-uml.jpg
deleted file mode 100644
index 0ae7067..0000000
Binary files a/docs/docs/doc_assets/tense-uml.jpg and /dev/null differ
diff --git a/docs/docs/documentation.md b/docs/docs/documentation.md
deleted file mode 100644
index 995c7d6..0000000
--- a/docs/docs/documentation.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# <--- IN-DEV --->
-![Image title](doc_assets/oops.jpg){ align=center }
-
-!!! warning
-    At the moment, the documentation is under development, please select the language that suits you and read 
-    the project's FAQ. There is a lot of useful information there.
-
-    ![language](doc_assets/language.png){ align=center }
-    
-    Thank you for attention to our project! ❤️
diff --git a/docs/docs/en/index.md b/docs/docs/en_faq.md
similarity index 95%
rename from docs/docs/en/index.md
rename to docs/docs/en_faq.md
index 0432758..101ce5e 100644
--- a/docs/docs/en/index.md
+++ b/docs/docs/en_faq.md
@@ -1,4 +1,4 @@
-<img src="../doc_assets/tense-logo.jpg" align="left" width="200px"/>
+<img src="../tense-logo.jpg" align="left" width="200px"/>
 Project: aiotense
 <br>
 License: Apache 2.0
@@ -30,13 +30,17 @@ Then recommend us to your friends :) And if you are still here - let's continue!
 # How is aiotense designed?
 `aiotense` designed to provide maximum **flexibility**, **practicality** and **convenience** to the user.
 Some logic is imported immediately as a class or function:
+
 ```py
-from aiotense import TenseParser, from_tense_file, from_tense_file_source
+from tense import TenseParser, from_tense_file, from_tense_file_source
 ```
 And some parts are imported as modules:
+
 ```py
-from aiotense import units, model, unit_of_work, resolvers, ...
-from aiotense.adapters import repository
+from tense import units, model, unit_of_work, resolvers,
+
+...
+from tense.adapters import repository
 ```
 Such an interface is made to avoid any cyclic imports and is the main style of the project.
 
@@ -44,9 +48,10 @@ Such an interface is made to avoid any cyclic imports and is the main style of t
 Let's start with the basics, let's say we have a string "1day1minute 20 seconds", how do we convert it to,
 as previously stated, the number of seconds or a datetime.timedelta object?
 - Let's consider!
+
 ```py
 import asyncio
-from aiotense import TenseParser
+from tense import TenseParser
 
 time_string = "1day1minute 20 seconds"
 
@@ -96,16 +101,18 @@ In addition to the "primitive" resolver, which is the default, there is also a "
     that the resolver has a base signature (arg1: str, arg2: model.Tense).
 
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, resolvers
-
->>> time_string = "1 day + 2 minutes and 5seconds"
-
->>> parser = TenseParser(
-...     TenseParser.DIGIT, 
-...     time_resolver=resolvers.smart_resolver,
-... )
->>> asyncio.run(parser.parse(time_string))
+>> > import asyncio
+>> > from tense import TenseParser, resolvers
+
+>> > time_string = "1 day + 2 minutes and 5seconds"
+
+>> > parser = TenseParser(
+    ...
+TenseParser.DIGIT,
+...
+time_resolver = resolvers.smart_resolver,
+                ... )
+>> > asyncio.run(parser.parse(time_string))
 86525
 ```
 # What is the "converter" argument of TenseParser?
@@ -163,16 +170,16 @@ functionality.
 
 ```py
 import asyncio
-from aiotense import TenseParser, units
-from aiotense.adapters import repository
+from tense import TenseParser, units
+from tense.adapters import repository
 
 tenses = repository.TenseRepository()
 (
     tenses.add_virtual_unit(
         units.VirtualUnit(aliases=["twoseconds"], duration=2)
     )
-    # or
-    .add_virtual_unit_dict(  # TypedDict
+        # or
+        .add_virtual_unit_dict(  # TypedDict
         {"aliases": ["threeseconds"], "duration": 3}
     )
 )
@@ -482,9 +489,10 @@ in turn, it parses specifically the string representation of the config.
     then it does not replace completely, but updates an already existing repository template using the dict.update() method.
 
 I will use `from_tense_file_source` to emulate the config, but you get the point.
+
 ```py
 import asyncio
-from aiotense import TenseParser, from_tense_file_source, resolvers
+from tense import TenseParser, from_tense_file_source, resolvers
 
 config_emulation = """
 [model.Tense]
@@ -502,8 +510,8 @@ duration = exp(year * 1000)
 
 time_string = "1 millennium and 20 seconds..."
 parser = TenseParser(
-    TenseParser.DIGIT, 
-    config=from_tense_file_source(config_emulation), 
+    TenseParser.DIGIT,
+    config=from_tense_file_source(config_emulation),
     time_resolver=resolvers.smart_resolver,
 )
 assert asyncio.run(parser.parse(time_string)) == 31536000020  # :D
@@ -515,8 +523,8 @@ configuration type **year * 10**) and add new values there!
 
 ```py
 import asyncio
-from aiotense import TenseParser, from_tense_file_source, resolvers
-from aiotense.service_layer.dot_tense.converters import VCONVERTER_CONSTS
+from tense import TenseParser, from_tense_file_source, resolvers
+from tense.service_layer.dot_tense.converters import VCONVERTER_CONSTS
 
 VCONVERTER_CONSTS.update({
     "decade": 60 * 60 * 24 * 365 * 10,
diff --git a/docs/docs/index.md b/docs/docs/index.md
index d1dcbee..99a7d3f 100644
--- a/docs/docs/index.md
+++ b/docs/docs/index.md
@@ -1,7 +1,4 @@
 <figure markdown>
-  ![Image title](doc_assets/tense-banner.jpg){align=center }
+  ![Image title](tense-banner.jpg){ align=center }
   <h1 align="center">Before you start using the documentation, it is recommended to read the FAQ of the project. Choose the language in which it will be convenient for you to read it!</h1>
 </figure>
-
-???+ help "How to select FAQ?"
-    ![language](doc_assets/language.png){ align=center }
diff --git a/docs/docs/ru/index.md b/docs/docs/ru_faq.md
similarity index 93%
rename from docs/docs/ru/index.md
rename to docs/docs/ru_faq.md
index 2bee358..64bd4b8 100644
--- a/docs/docs/ru/index.md
+++ b/docs/docs/ru_faq.md
@@ -1,5 +1,5 @@
-<img src="../doc_assets/tense-logo.jpg" align="left" width="200px"/>
-Проект: aiotense
+<img src="../tense-logo.jpg" align="left" width="200px"/>
+Проект: tense
 <br>
 Лицензия: Apache 2.0
 <br>
@@ -30,13 +30,17 @@ Typing: Typed
 # Как спроектирован aiotense?
 `aiotense` спроектирован так, чтоб обеспечить максимальную **гибкость**, **практичность** и **удобство** пользователю.
 Некоторая логика импортируется сразу, как класс или функция:
+
 ```py
-from aiotense import TenseParser, from_tense_file, from_tense_file_source
+from tense import TenseParser, from_tense_file, from_tense_file_source
 ```
 А некоторые части импортируются как модули:
+
 ```py
-from aiotense import units, model, unit_of_work, resolvers, ...
-from aiotense.adapters import repository
+from tense import units, model, unit_of_work, resolvers,
+
+...
+from tense.adapters import repository
 ```
 Такой интерфейс сделан во избежание каких-либо циклических импортов и является основным стилем проекта.
 
@@ -44,34 +48,36 @@ from aiotense.adapters import repository
 Начнём с основ, доспустим, у нас есть строка "1день1минута 20 секунд", как нам её преобразовать в, 
 как ранее говорилось, кол-во секунд или объект datetime.timedelta?
 - Давайте рассмотрим!
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser
+>> > import asyncio
+>> > from tense import TenseParser
 
->>> time_string = "1день1минута 20 секунд"
+>> > time_string = "1день1минута 20 секунд"
 
->>> parser = TenseParser(TenseParser.DIGIT)
->>> asyncio.run(parser.parse(time_string))
+>> > parser = TenseParser(TenseParser.DIGIT)
+>> > asyncio.run(parser.parse(time_string))
 0
 ```
 Подождите-ка, 0? И это всё? - Нет. По умолчанию поддерживаются только некоторые алиасы английского 
 языка, все остальные вы можете добавить сами! См. пример ниже:
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, units
->>> from aiotense.adapters import repository
+>> > import asyncio
+>> > from tense import TenseParser, units
+>> > from tense.adapters import repository
 
->>> tenses = repository.TenseRepository()
->>> (
-...    tenses.add_aliases_to(units.Day, ["день"])
-...    .add_aliases_to(units.Minute, ["минута"])
-...    .add_aliases_to(units.Second, ["секунд"])
-... )
+>> > tenses = repository.TenseRepository()
+>> > (
+    ...    tenses.add_aliases_to(units.Day, ["день"])
+    ....add_aliases_to(units.Minute, ["минута"])
+    ....add_aliases_to(units.Second, ["секунд"])
+    ... )
 
->>> time_string = "1день1минута 20 секунд"
+>> > time_string = "1день1минута 20 секунд"
 
->>> parser = TenseParser(TenseParser.DIGIT, config=tenses.config)
->>> asyncio.run(parser.parse(time_string))
+>> > parser = TenseParser(TenseParser.DIGIT, config=tenses.config)
+>> > asyncio.run(parser.parse(time_string))
 86480
 ```
 Вот, другое дело!
@@ -117,25 +123,28 @@ TenseParser.__new__(
     что у резольвера есть базовая сигнатура (arg1: str, arg2: model.Tense).
 
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, units, resolvers
->>> from aiotense.adapters import repository
-
->>> tenses = repository.TenseRepository()
->>> (
- ...    tenses.add_aliases_to(units.Day, ["день"])
- ...    .add_aliases_to(units.Minute, ["минуты"])
-...     .add_aliases_to(units.Second, ["секунд"])
-... )
-
->>> time_string = "1 день + 2 минуты и 5 секунд"
-
->>> parser = TenseParser(
-...     TenseParser.DIGIT, 
-...     config=tenses.config,
-...     time_resolver=resolvers.smart_resolver,
-... )
->>> asyncio.run(parser.parse(time_string))
+>> > import asyncio
+>> > from tense import TenseParser, units, resolvers
+>> > from tense.adapters import repository
+
+>> > tenses = repository.TenseRepository()
+>> > (
+    ...    tenses.add_aliases_to(units.Day, ["день"])
+    ....add_aliases_to(units.Minute, ["минуты"])
+    ....add_aliases_to(units.Second, ["секунд"])
+    ... )
+
+>> > time_string = "1 день + 2 минуты и 5 секунд"
+
+>> > parser = TenseParser(
+    ...
+TenseParser.DIGIT,
+...
+config = tenses.config,
+         ...
+time_resolver = resolvers.smart_resolver,
+                ... )
+>> > asyncio.run(parser.parse(time_string))
 86525
 ```
 # А что за аргумент "converter" у TenseParser?
@@ -193,16 +202,16 @@ TenseParser.__new__(
 
 ```py
 import asyncio
-from aiotense import TenseParser, units
-from aiotense.adapters import repository
+from tense import TenseParser, units
+from tense.adapters import repository
 
 tenses = repository.TenseRepository()
 (
     tenses.add_virtual_unit(
         units.VirtualUnit(aliases=["двесекунды"], duration=2)
     )
-    # или
-    .add_virtual_unit_dict(  # TypedDict
+        # или
+        .add_virtual_unit_dict(  # TypedDict
         {"aliases": ["трисекунды"], "duration": 3}
     )
 )
@@ -511,9 +520,10 @@ assert asyncio.run(parser.parse("1 двесекунды 1 трисекунды")
     то он не заменяется полностью, а обновляет уже существующий шаблон репозитория с помощью метода dict.update().
 
 Я буду использовать `from_tense_file_source` для эмуляции конфига, но суть Вы поняли.
+
 ```py
 import asyncio
-from aiotense import TenseParser, from_tense_file_source, resolvers
+from tense import TenseParser, from_tense_file_source, resolvers
 
 config_emulation = """
 [model.Tense]
@@ -531,8 +541,8 @@ duration = exp(year * 1000)
 
 time_string = "1 тысячелетие и 20 секунд..."
 parser = TenseParser(
-    TenseParser.DIGIT, 
-    config=from_tense_file_source(config_emulation), 
+    TenseParser.DIGIT,
+    config=from_tense_file_source(config_emulation),
     time_resolver=resolvers.smart_resolver,
 )
 assert asyncio.run(parser.parse(time_string)) == 31536000020  # :D
@@ -544,8 +554,8 @@ assert asyncio.run(parser.parse(time_string)) == 31536000020  # :D
 
 ```py
 import asyncio
-from aiotense import TenseParser, from_tense_file_source, resolvers
-from aiotense.service_layer.dot_tense.converters import VCONVERTER_CONSTS
+from tense import TenseParser, from_tense_file_source, resolvers
+from tense.service_layer.dot_tense.converters import VCONVERTER_CONSTS
 
 VCONVERTER_CONSTS.update({
     "десятилетие": 60 * 60 * 24 * 365 * 10,
diff --git a/docs/docs/doc_assets/tense-banner.jpg b/docs/docs/tense-banner.jpg
similarity index 100%
rename from docs/docs/doc_assets/tense-banner.jpg
rename to docs/docs/tense-banner.jpg
diff --git a/docs/docs/doc_assets/tense-logo-filled-circle.png b/docs/docs/tense-logo-filled-circle.png
similarity index 100%
rename from docs/docs/doc_assets/tense-logo-filled-circle.png
rename to docs/docs/tense-logo-filled-circle.png
diff --git a/docs/docs/doc_assets/tense-logo.jpg b/docs/docs/tense-logo.jpg
similarity index 100%
rename from docs/docs/doc_assets/tense-logo.jpg
rename to docs/docs/tense-logo.jpg
diff --git a/docs/docs/uk/index.md b/docs/docs/uk_faq.md
similarity index 93%
rename from docs/docs/uk/index.md
rename to docs/docs/uk_faq.md
index a68620b..3cb56a9 100644
--- a/docs/docs/uk/index.md
+++ b/docs/docs/uk_faq.md
@@ -1,4 +1,4 @@
-<img src="../doc_assets/tense-logo.jpg" align="left" width="200px"/>
+<img src="../tense-logo.jpg" align="left" width="200px"/>
 Проект: aiotense
 <br>
 Ліцензія: Apache 2.0
@@ -27,16 +27,20 @@ Typing: Typed
 типу "1день1хвилина 20 секунд" в кількість секунд або об'єкт datetime.timedelta? Ні?
 Тоді порадьте нас своїм друзям:) А якщо ви все ще тут – давайте продовжимо!
 
-# Как спроектирован aiotense?
+# Як спроектований aiotense?
 `aiotense` спроектований так, щоб забезпечити максимальну **гнучкість**, **практичність** та **зручність** користувачеві.
 Деяка логіка імпортується одразу, як клас або функція:
+
 ```py
-from aiotense import TenseParser, from_tense_file, from_tense_file_source
+from tense import TenseParser, from_tense_file, from_tense_file_source
 ```
 А деякі частини імпортуються як модулі:
+
 ```py
-from aiotense import units, model, unit_of_work, resolvers, ...
-from aiotense.adapters import repository
+from tense import units, model, unit_of_work, resolvers,
+
+...
+from tense.adapters import repository
 ```
 Такий інтерфейс зроблено, щоб уникнути будь-яких циклічних імпортів і є основним стилем проекту.
 
@@ -44,34 +48,36 @@ from aiotense.adapters import repository
 Почнемо з основ, припустимо, у нас є рядок "1день1хвилина 20 секунд", як нам її перетворити на,
 як раніше говорилося, у секунд або об'єкт datetime.timedelta?
 - Давайте розглянемо!
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser
+>> > import asyncio
+>> > from tense import TenseParser
 
->>> time_string = "1день1хвилина 20 секунд"
+>> > time_string = "1день1хвилина 20 секунд"
 
->>> parser = TenseParser(TenseParser.DIGIT)
->>> asyncio.run(parser.parse(time_string))
+>> > parser = TenseParser(TenseParser.DIGIT)
+>> > asyncio.run(parser.parse(time_string))
 0
 ```
 Зачекайте, 0? І це все? - Ні. За замовчуванням підтримуються лише деякі аліаси англійської
 мови, решту ви можете додати самі! Див. приклад нижче:
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, units
->>> from aiotense.adapters import repository
+>> > import asyncio
+>> > from tense import TenseParser, units
+>> > from tense.adapters import repository
 
->>> tenses = repository.TenseRepository()
->>> (
-...    tenses.add_aliases_to(units.Day, ["день"])
-...    .add_aliases_to(units.Minute, ["хвилина"])
-...    .add_aliases_to(units.Second, ["секунд"])
-... )
+>> > tenses = repository.TenseRepository()
+>> > (
+    ...    tenses.add_aliases_to(units.Day, ["день"])
+    ....add_aliases_to(units.Minute, ["хвилина"])
+    ....add_aliases_to(units.Second, ["секунд"])
+    ... )
 
->>> time_string = "1день1хвилина 20 секунд"
+>> > time_string = "1день1хвилина 20 секунд"
 
->>> parser = TenseParser(TenseParser.DIGIT, config=tenses.config)
->>> asyncio.run(parser.parse(time_string))
+>> > parser = TenseParser(TenseParser.DIGIT, config=tenses.config)
+>> > asyncio.run(parser.parse(time_string))
 86480
 ```
 Ось інша справа!
@@ -103,7 +109,7 @@ TenseParser.__new__(
 Якщо ваш рядок є чимось подібним до "1 день + 2 хвилини і 5 секунд", то, швидше за все, у вас буде
 конвертувати тільки останнє значення. Запитаєте, чому це так? - Справа в **резольвері** (time_resolver).
 За замовчуванням стоїть резольвер, який відмінно справляється з примітивними рядками, цей же рядок є
-комплексним (див. наприкінці FAQ терміни).
+комплексним (див. наприкінці FAQ Глосарій).
 
 Крім "примітивного" резольвера, що стоїть за замовчуванням, так само є і "розумний".
 
@@ -118,8 +124,8 @@ TenseParser.__new__(
 
 ```py
 >>> import asyncio
->>> from aiotense import TenseParser, units, resolvers
->>> from aiotense.adapters import repository
+>>> from tense import TenseParser, units, resolvers
+>>> from tense.adapters import repository
 
 >>> tenses = repository.TenseRepository()
 >>> (
@@ -193,16 +199,16 @@ TenseParser.__new__(
 
 ```py
 import asyncio
-from aiotense import TenseParser, units
-from aiotense.adapters import repository
+from tense import TenseParser, units
+from tense.adapters import repository
 
 tenses = repository.TenseRepository()
 (
     tenses.add_virtual_unit(
         units.VirtualUnit(aliases=["двісекунди"], duration=2)
     )
-    # або
-    .add_virtual_unit_dict(  # TypedDict
+        # або
+        .add_virtual_unit_dict(  # TypedDict
         {"aliases": ["трисекунди"], "duration": 3}
     )
 )
@@ -237,8 +243,8 @@ assert asyncio.run(parser.parse("1 двісекунди 1 трисекунди")
     ```
 === "ListValueConverter"
     !!! danger
-        Списки в `aiotense` по паттерну схожи с кортежами python. Если у вас 
-        всего один элемент списка, то вы должны указать его с запятой, иначе возникнет ошибка.
+        Списки в `aiotense` по патерну схожі з кортежами python. Якщо у вас
+        всього один елемент списку, то ви повинні вказати його з комою, інакше виникне помилка.
 
     ```markdown
     Відповідає всі спискові значення об'єктів (variable = 1, 2, 3).
@@ -512,9 +518,10 @@ assert asyncio.run(parser.parse("1 двісекунди 1 трисекунди")
     він не замінюється повністю, а оновлює вже існуючий шаблон репозиторію за допомогою методу dict.update().
 
 Я використовуватиму `from_tense_file_source` для емуляції конфіга, але суть Ви зрозуміли.
+
 ```py
 import asyncio
-from aiotense import TenseParser, from_tense_file_source, resolvers
+from tense import TenseParser, from_tense_file_source, resolvers
 
 config_emulation = """
 [model.Tense]
@@ -545,8 +552,8 @@ assert asyncio.run(parser.parse(time_string)) == 31536000020  # :D
 
 ```py
 import asyncio
-from aiotense import TenseParser, from_tense_file_source, resolvers
-from aiotense.service_layer.dot_tense.converters import VCONVERTER_CONSTS
+from tense import TenseParser, from_tense_file_source, resolvers
+from tense.service_layer.dot_tense.converters import VCONVERTER_CONSTS
 
 VCONVERTER_CONSTS.update({
     "десятиліття": 60 * 60 * 24 * 365 * 10,
@@ -569,7 +576,7 @@ parser = TenseParser(
 assert asyncio.run(parser.parse(time_string)).days == 36500
 ```
 
-# Терміни
+# Глосарій
 !!! info "Примітивний рядок часу"
     === "Визначення"
         ```markdown
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index fa8b024..63e754c 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -1,17 +1,20 @@
-site_name: aiotense
-site_url: https://aiotense.com/
-repo_url: https://github.com/Animatea/aiotense
-repo_name: Animatea/aiotense
-edit_uri: https://github.com/Animatea/aiotense/tree/main/docs/docs
+site_name: "Aiotense docs"
+site_url: https://tense.com/
+repo_url: https://github.com/Animatea/tense
+repo_name: Animatea/tense
+edit_uri: https://github.com/Animatea/tense/tree/main/docs/docs
 
 nav:
     - Home: index.md
-    - Documentation: documentation.md
+    - "FAQ":
+        - "Ukraine": uk_faq.md
+        - "English": en_faq.md
+        - "Russian": ru_faq.md
 
 theme:
     name: material
     language: en
-    favicon: doc_assets/tense-logo-filled-circle.png
+    favicon: tense-logo-filled-circle.png
     palette:
       - scheme: default
         primary: deep orange
@@ -47,18 +50,6 @@ copyright: >
   Copyright &copy; 2022 Animatea –
   <a href="https://github.com/Animatea/aiotense/blob/main/LICENSE">License</a>
 
-extra:
-  alternate:
-    - name: Ukrainian-FAQ
-      link: /uk/
-      lang: uk
-    - name: English-FAQ
-      link: /en/
-      lang: en
-    - name: Russian-FAQ
-      link: /ru/
-      lang: ru
-
 plugins:
   - search:
       lang:
diff --git a/examples/.aiotense b/examples/.tense
similarity index 100%
rename from examples/.aiotense
rename to examples/.tense
diff --git a/examples/advanced_custom_converters.py b/examples/advanced_custom_converters.py
index 4846a56..e59764b 100644
--- a/examples/advanced_custom_converters.py
+++ b/examples/advanced_custom_converters.py
@@ -1,18 +1,15 @@
-import asyncio
 from typing import Any
 
-from aiotense import TenseParser
-from aiotense.application.ports import converters as abc_converters
+from tense import TenseParser
+from tense.application.ports import converters as abc_converters
 
 
 class DoubleConverter(abc_converters.AbstractConverter[int | float]):  # <--|
-    async def convert(self, value: Any) -> int | float:  # <----------------|
+    def convert(self, value: Any) -> int | float:  # <----------------------|
         if not isinstance(value, (int, float)):
             raise ValueError("Value must be instance of (int, float).")
         return value * 2
 
 
 parser = TenseParser(TenseParser.DIGIT, converter=DoubleConverter())
-assert (
-    asyncio.run(parser.parse("1 minute")) == 120
-)  # 1 minute = 60 seconds, 60*2 = 120.
+assert parser.parse("1 minute") == 120  # 1 minute = 60 seconds, 60*2 = 120.
diff --git a/examples/advanced_custom_parser.py b/examples/advanced_custom_parser.py
deleted file mode 100644
index f282c43..0000000
--- a/examples/advanced_custom_parser.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from typing import Any
-
-from aiotense import TenseParser
-from aiotense.application.ports import parsers as abc_parsers
-from aiotense.service_layer import functional
-
-
-class CachedDigitParser(abc_parsers.AbstractParser):
-    @functional.async_lru()  # type: ignore[misc]
-    async def _parse(self, raw_str: str) -> Any:
-        # Some expensive stuff here...
-        ...
-
-
-cached_digit_parser = TenseParser(CachedDigitParser)
-# asyncio.run(cached_digit_parser.parse(string)) ...
diff --git a/examples/advanced_custom_resolver.py b/examples/advanced_custom_resolver.py
index 344b685..e56aba7 100644
--- a/examples/advanced_custom_resolver.py
+++ b/examples/advanced_custom_resolver.py
@@ -1,13 +1,12 @@
 from __future__ import annotations
 
-# !!! Note: resolver functions accepts (str, model.Tense) parameters and returns list of strings.
-import asyncio
-from typing import TYPE_CHECKING
+# !!! Note: resolver functions accepts (str, model.Tense) parameters and returns iterator of strings.
+from typing import TYPE_CHECKING, Iterator
 
-from aiotense import TenseParser, resolvers
+from tense import TenseParser, resolvers
 
 if TYPE_CHECKING:
-    from aiotense.domain import model
+    from tense.domain import model
 
 _REVERSED_SECONDS = "seconds"[::-1]
 _REVERSED_MINUTE = "minute"[::-1]
@@ -16,13 +15,13 @@
 reversed_time_string = "1" + _REVERSED_MINUTE + "10   " + _REVERSED_SECONDS
 
 
-def reverse_resolver(raw_str: str, _: model.Tense) -> list[str]:
+def reverse_resolver(raw_str: str, _: model.Tense) -> Iterator[str]:
     # Resolves reversed units of time.
-    return [
+    return (
         (w[::-1] if not w.isdigit() else w)
-        for w in resolvers.smart_resolver(raw_str, _)
-    ]
+        for w in resolvers.basic_resolver(raw_str, _)
+    )
 
 
 parser = TenseParser(TenseParser.DIGIT, time_resolver=reverse_resolver)
-assert asyncio.run(parser.parse(reversed_time_string)) == 70
+assert parser.parse(reversed_time_string) == 70
diff --git a/examples/basics.py b/examples/basics.py
index 7af7c32..15a6f0d 100644
--- a/examples/basics.py
+++ b/examples/basics.py
@@ -1,11 +1,9 @@
-import asyncio
-
-from aiotense import TenseParser
+from tense import TenseParser
 
 time_string = "1min10second 1 second"
 
 digit_parser = TenseParser(TenseParser.DIGIT)  # returns int
-assert asyncio.run(digit_parser.parse(time_string)) == 71
+assert digit_parser.parse(time_string) == 71
 
 delta_parser = TenseParser(TenseParser.TIMEDELTA)  # returns datetime.timedelta
-assert str(asyncio.run(delta_parser.parse(time_string))) == "0:01:11"
+assert str(delta_parser.parse(time_string)) == "0:01:11"
diff --git a/examples/basics_resolver.py b/examples/basics_resolver.py
index fc27f9f..7f9a090 100644
--- a/examples/basics_resolver.py
+++ b/examples/basics_resolver.py
@@ -1,17 +1,15 @@
-import asyncio
-
-from aiotense import TenseParser, resolvers
+from tense import TenseParser, resolvers
 
 complex_string = "1minute and 10 seconds + 5 seconds"
 
 digit_parser = TenseParser(TenseParser.DIGIT)
-assert asyncio.run(digit_parser.parse(complex_string)) != 75  # :C sad, but...
+assert digit_parser.parse(complex_string) != 75  # :C sad, but...
 
 digit_parser.resolver = resolvers.smart_resolver  # <-----------------------|
-assert asyncio.run(digit_parser.parse(complex_string)) == 75  # =) happy    |
+assert digit_parser.parse(complex_string) == 75  # =) happy                 |
 # or                                                                      # | equals
-new_digit_parser = TenseParser(                                           # | to
-    TenseParser.DIGIT,                                                    # |
+new_digit_parser = TenseParser(  # | to
+    TenseParser.DIGIT,  # |
     time_resolver=resolvers.smart_resolver,  # <----------------------------|
 )
-assert asyncio.run(new_digit_parser.parse(complex_string)) == 75  # =) happy
+assert new_digit_parser.parse(complex_string) == 75  # =) happy
diff --git a/examples/configuration_atomic.py b/examples/configuration_atomic.py
index e8c6a14..292e2ee 100644
--- a/examples/configuration_atomic.py
+++ b/examples/configuration_atomic.py
@@ -1,14 +1,14 @@
-from aiotense import unit_of_work, units
+from tense import unit_of_work, units
 
 with unit_of_work.TenseUnitOfWork() as uow:
     # Updating `multiplier` setting
     uow.update_config({"model.Tense": {"multiplier": 2}})
     # Works.
-    assert uow.tenses.config["model.Tense"]["multiplier"] == 2
+    assert uow.products.config["model.Tense"]["multiplier"] == 2
 
 with unit_of_work.TenseUnitOfWork() as uow:
     # By default `s` is alias of `units.Second`.
-    initial_aliases = uow.tenses.config["units.Second"]["aliases"]
+    initial_aliases = uow.products.config["units.Second"]["aliases"]
     assert "s" in initial_aliases
 
     # Replacing alias `s` to `ss`
@@ -19,7 +19,7 @@
 
 with unit_of_work.TenseUnitOfWork() as uow:
     # By default `s` and `second` are aliases of `units.Second`.
-    initial_aliases = uow.tenses.config["units.Second"]["aliases"]
+    initial_aliases = uow.products.config["units.Second"]["aliases"]
 
     assert "ss" in initial_aliases
     assert "second" in initial_aliases
diff --git a/examples/configuration_load.py b/examples/configuration_load.py
index f29ded6..67055e3 100644
--- a/examples/configuration_load.py
+++ b/examples/configuration_load.py
@@ -1,9 +1,7 @@
-import asyncio
+from tense import TenseParser, from_tense_file, from_tense_file_source
 
-from aiotense import TenseParser, from_tense_file, from_tense_file_source
-
-parser = TenseParser(TenseParser.DIGIT, config=from_tense_file(".aiotense"))
-assert asyncio.run(parser.parse("1 decade")) > 0
+parser = TenseParser(TenseParser.DIGIT, tenses=from_tense_file(".tense"))
+assert parser.parse("1 decade") > 0
 
 config_emulation = """
 [model.Tense]
@@ -13,6 +11,7 @@
 aliases = decade, dec, decs, decades
 """
 other_parser = TenseParser(
-    TenseParser.DIGIT, config=from_tense_file_source(config_emulation)
+    TenseParser.DIGIT,
+    tenses=from_tense_file_source(config_emulation),
 )
-assert asyncio.run(other_parser.parse("1 decade")) > 0
+assert other_parser.parse("1 decade") > 0
diff --git a/examples/configuration_repository.py b/examples/configuration_repository.py
index e47276e..5198803 100644
--- a/examples/configuration_repository.py
+++ b/examples/configuration_repository.py
@@ -1,8 +1,6 @@
-import asyncio
-
-from aiotense import TenseParser, resolvers
-from aiotense.adapters import repository
-from aiotense.domain import units
+from tense import TenseParser, resolvers
+from tense.adapters import repository
+from tense.domain import units
 
 tenses = repository.TenseRepository()
 tenses2 = repository.TenseRepository()
@@ -40,7 +38,7 @@ def add_virtual_units_demonstration() -> None:
         time_resolver=resolvers.smart_resolver,
     )
 
-    assert asyncio.run(parser.parse(integration_time_string)).days > 0
+    assert parser.parse(integration_time_string).days > 0
 
 
 if __name__ == "__main__":
diff --git a/examples/safe_eval_example.py b/examples/safe_eval_example.py
new file mode 100644
index 0000000..55dbca5
--- /dev/null
+++ b/examples/safe_eval_example.py
@@ -0,0 +1,19 @@
+from tense.service_layer import safe_eval as time_exp_eval
+
+CONSTANT = 3.14
+
+# It works :)
+exp = "2 + 2 * 2"
+assert time_exp_eval.SafelyExpEvalute(exp).safe_evalute() == 6
+
+# Flexible.
+deps = {"minute": 60}
+exp = "minute * 60"
+assert time_exp_eval.SafelyExpEvalute(exp, eval_locals=deps).safe_evalute() == 3600
+
+# Safety.
+time_exp_eval.SafelyExpEvalute("globals().pop('CONSTANT')")
+assert "CONSTANT" in globals()
+
+time_exp_eval.SafelyExpEvalute("delattr(__builtins__, 'int')")
+assert getattr(__builtins__, "int") is int
diff --git a/examples/service_layer_examples.py b/examples/service_layer_examples.py
deleted file mode 100644
index 82e8db7..0000000
--- a/examples/service_layer_examples.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from aiotense.service_layer import safe_eval as time_exp_eval
-
-CONSTANT = 3.14
-
-
-def time_exp_eval_demonstration() -> None:
-    # It works :)
-    exp = "2 + 2 * 2"
-    assert time_exp_eval.SafelyExpEvalute(exp).safe_evalute() == 6
-
-    # Flexible.
-    deps = {"minute": 60}
-    exp = "minute * 60"
-    assert time_exp_eval.SafelyExpEvalute(exp, eval_locals=deps).safe_evalute() == 3600
-
-    # Safety.
-    time_exp_eval.SafelyExpEvalute("globals().pop('CONSTANT')")
-    assert "CONSTANT" in globals()
-
-
-if __name__ == "__main__":
-    time_exp_eval_demonstration()
diff --git a/i18n/ru_README.md b/i18n/ru_README.md
index e51d363..8b07c3a 100644
--- a/i18n/ru_README.md
+++ b/i18n/ru_README.md
@@ -1,6 +1,6 @@
 <div id="top"></div>
 <img src="../assets/tense-logo.jpg" align="left" width="200px"/>
-Проект: aiotense
+Проект: tense
 <br>
 Лицензия: Apache 2.0
 <br>
@@ -16,11 +16,11 @@ Typing: Аннотирован
 <br />
     <p align="center">
     <br />
-    <a href="https://animatea.github.io/aiotense/">Документация</a>
+    <a href="https://animatea.github.io/tense/">Документация</a>
     ·
-    <a href="https://github.com/Animatea/aiotense/issues">Сообщить о баге</a>
+    <a href="https://github.com/Animatea/tense/issues">Сообщить о баге</a>
     ·
-    <a href="https://github.com/Animatea/aiotense/issues">Предложить идею</a>
+    <a href="https://github.com/Animatea/tense/issues">Предложить идею</a>
     </p>
 <div id="top"></div>
 <p align="center">
@@ -58,15 +58,16 @@ Typing: Аннотирован
     <li><a href="#лицензия">Лицензия</a></li>
     <li><a href="#контакт">Контакт</a></li>
     <li><a href="#благодарности">Благодарности</a></li>
+    <li><a href="#история">История проекта</a></li>
   </ol>
 </details>
 
 ## О проекте
-<a href="https://circleci.com/gh/Animatea/aiotense/tree/main"><img height="20" src="https://circleci.com/gh/Animatea/aiotense/tree/main.svg?style=svg&circle-token=066eb74fc70db6eeaa6df5ade9e3d2df131b1de1"></a>
-<a href="https://pypi.org/project/aiotense/"><img height="20" alt="PyPi" src="https://img.shields.io/pypi/v/aiotense"></a>
+<a href="https://circleci.com/gh/Animatea/tense/tree/main"><img height="20" src="https://dl.circleci.com/status-badge/img/gh/Animatea/tense/tree/main.svg?style=svg"></a>
+<a href="https://pypi.org/project/tense/"><img height="20" alt="PyPi" src="https://img.shields.io/pypi/v/tense"></a>
 <a href="https://pypi.org/project/mypy/"><img height="20" alt="Mypy badge" src="http://www.mypy-lang.org/static/mypy_badge.svg"></a>
 <a href="https://github.com/psf/black"><img height="20" alt="Black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
-<a href="https://pycqa.github.io/isort/"><img height="20" alt="Supported python versions" src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336">
+<a href="https://pycqa.github.io/isort/"><img height="20" alt="Supported python versions" src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336"></a>
 
 ### Добро пожаловать
 > Нужно ли Вам было конвертировать, например, строку "1д1минута 2 сек" 
@@ -74,41 +75,36 @@ Typing: Аннотирован
 
 Нет? Тогда посоветуйте наш проект своим друзьям :) Если вы всё ещё здесь - давайте двигаться дальше!
 
-<h5 align="center">UML Диаграма с основной логикой проекта</h5>
-<img src="../assets/tense-uml.jpg" align="left"/>
-
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/вернуться в-начало-green?style=social&logo=github"></a></p>
 
 ## Приступить к использованию
 ### PyPi
 ```bash
-$ pip3 install aiotense
+$ pip3 install tense
 ```
 
 ### Poetry
 ```bash
-$ poetry add aiotense
+$ poetry add tense
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/вернуться в-начало-green?style=social&logo=github"></a></p>
 
 ## Использование
 ### Основные возможности
+
 ```py
-import asyncio
 import datetime
-from aiotense import TenseParser
+from tense import TenseParser
 
 time_string = "1d2minutes 5 sec"
 
 # <-- Digit parser -->
 digit_parser = TenseParser(TenseParser.DIGIT)
-digit_value = asyncio.run(digit_parser.parse(time_string))
-# <-- Assertions -->
-assert digit_value == 86525
+assert digit_parser.parse(time_string) == 86525
 
 # <-- Timedelta parser -->
 delta_parser = TenseParser(TenseParser.TIMEDELTA)
-delta_value = asyncio.run(delta_parser.parse(time_string))
+delta_value = delta_parser.parse(time_string)
 # <-- Assertions -->
 assert isinstance(delta_value, datetime.timedelta)
 assert str(delta_value) == "1 day, 0:02:05"
@@ -116,9 +112,9 @@ assert str(delta_value) == "1 day, 0:02:05"
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/вернуться в-начало-green?style=social&logo=github"></a></p>
 
 ### Изменение существующих параметров
+
 ```py
-import asyncio
-from aiotense import TenseParser, from_tense_file_source
+from tense import TenseParser, from_tense_file_source
 
 config_emulation = """
 [model.Tense]
@@ -132,18 +128,17 @@ aliases = моя_минута, моя_мин, моих_мин, минут
 """
 parser = TenseParser(
     TenseParser.TIMEDELTA,
-    config=from_tense_file_source(config_emulation),
+    tenses=from_tense_file_source(config_emulation),
 )
-delta_value = asyncio.run(parser.parse("1 моя_мин 10моих_мин 9  минут"))
-# <-- Assertions -->
-assert str(delta_value) ==  "1:20:00" # (каждая 120 * 2)
+delta_value = parser.parse("1 моя_мин 10моих_мин 9  минут")
+assert str(delta_value) == "1:20:00"  # (каждая 120 * 2)
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/вернуться в-начало-green?style=social&logo=github"></a></p>
 
 ### Добавление новых настроек
+
 ```py
-import asyncio
-from aiotense import TenseParser, from_tense_file_source
+from tense import TenseParser, from_tense_file_source
 
 config_emulation = """
 [model.Tense]  # Этот заголовок обязателен.
@@ -163,10 +158,9 @@ aliases = десятилетие, десятилетий
 
 parser = TenseParser(
     TenseParser.TIMEDELTA,
-    config=from_tense_file_source(config_emulation),
+    tenses=from_tense_file_source(config_emulation),
 )
-delta_value = asyncio.run(parser.parse("1год 10 десятилетий5   секунд"))
-# <-- Assertions -->
+delta_value = parser.parse("1год 10 десятилетий5   секунд")
 assert str(delta_value) == "36865 days, 0:00:05"
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/вернуться в-начало-green?style=social&logo=github"></a></p>
@@ -174,56 +168,56 @@ assert str(delta_value) == "36865 days, 0:00:05"
 ### FAQ
 Но что если нам нужно парсить строку типа: "1day and 10 minutes + 5 seconds"?
 Давайте посмотрим:
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser
+>> > from tense import TenseParser
 
->>> complex_string = "1day and 10 minutes + 5 seconds"
+>> > complex_string = "1day and 10 minutes + 5 seconds"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA)
->>> asyncio.run(parser.parse(complex_string))
+>> > parser = TenseParser(TenseParser.TIMEDELTA)
+>> > parser.parse(complex_string)
 '0:00:05'
 ```
 Подождите... Что? 5 секунд? Но там же дни и минуты...
-- Всё окей, вы же используете гибкий aiotense! Проблему решить можно двумя способами:
+- Всё окей, вы же используете гибкий tense! Проблему решить можно двумя способами:
   1) Вы пишете свой time_resolver
-  2) Вы выбираете из уже существующих aiotense.resolvers
+  2) Вы выбираете из уже существующих tense.resolvers
 
 Давайте продемонстрирую!
 Я буду использовать второй вариант, так как существующие резольверы мне подходят.
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, resolvers
+>> > from tense import TenseParser, resolvers
 
->>> complex_string = "1day and 10 minutes + 5 seconds"
+>> > complex_string = "1day and 10 minutes + 5 seconds"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
->>> asyncio.run(parser.parse(complex_string)) 
+>> > parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
+>> > parser.parse(complex_string)
 '1 day, 0:10:05'
 ```
 Ну, так-то лучше!
 
-**aiotense.application.resolvers.smart_resolver()** так же является не чувствительным к регистру!
+**tense.application.resolvers.smart_resolver()** так же является не чувствительным к регистру!
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, resolvers
+>> > from tense import TenseParser, resolvers
 
->>> complex_string = "1DAY and 10 MINUTES + 5 SECONDS"
+>> > complex_string = "1DAY and 10 MINUTES + 5 SECONDS"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
->>> asyncio.run(parser.parse(complex_string)) 
+>> > parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
+>> > parser.parse(complex_string)
 '1 day, 0:10:05'
 ```
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/вернуться в-начало-green?style=social&logo=github"></a></p>
 
 ## Примеры
-Если Вы думаете, что на этом возможности aiotense заканчиваются, то вы ошибаетесь!
-Возможностей aiotense слишком много для README, поэтому я предлагаю Вам продолжить просмотр 
+Если Вы думаете, что на этом возможности tense заканчиваются, то вы ошибаетесь!
+Возможностей tense слишком много для README, поэтому я предлагаю Вам продолжить просмотр 
 примеров использования тут:
 <p align="center">
 <br />
-<a href="https://github.com/Animatea/aiotense/tree/main/examples">Aiotense Примеры</a>
+<a href="https://github.com/Animatea/tense/tree/main/examples">Tense Примеры</a>
 </p>
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/вернуться в-начало-green?style=social&logo=github"></a></p>
 
@@ -244,7 +238,7 @@ assert str(delta_value) == "36865 days, 0:00:05"
 <!-- LICENSE -->
 ## Лицензия
 
-Распространяется под лицензией Apache 2.0. См. детальнее [`LICENSE`](https://github.com/Animatea/aiotense/blob/main/LICENSE).
+Распространяется под лицензией Apache 2.0. См. детальнее [`LICENSE`](https://github.com/Animatea/tense/blob/main/LICENSE).
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/вернуться в-начало-green?style=social&logo=github"></a></p>
 
@@ -268,3 +262,17 @@ assert str(delta_value) == "36865 days, 0:00:05"
 * [MkDocs Material](https://squidfunk.github.io/mkdocs-material/)
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/вернуться в-начало-green?style=social&logo=github"></a></p>
+
+## История
+Изначально проект был сделан асинхронным, что существенно замедляло процесс парсинга, ведь tense является CPU-bound модулем.
+
+Отрефакторив проект, мы получили `~x22.31` ускорение в обработке сложных строк (с помощью `smart_resolver`).
+> Было: `~0.00095030...`μs | Стало: `~0.00004260...`μs
+
+И `~x38.28` ускорение в обработке простых строк, соответственно (с помощью `basic_resolver`).
+> Было: `~0.00062400...`μs | Стало: `~0.00001630...`μs
+
+Предыдущая (асинхронная) версия по-прежнему доступна в ветках репозитория - https://github.com/Animatea/tense/tree/async-final, 
+но сайт с документацией будет изменён.
+
+<p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/вернуться в-начало-green?style=social&logo=github"></a></p>
diff --git a/i18n/ua_README.md b/i18n/ua_README.md
index 35b30c2..40e0257 100644
--- a/i18n/ua_README.md
+++ b/i18n/ua_README.md
@@ -1,6 +1,6 @@
 <div id="top"></div>
 <img src="../assets/tense-logo.jpg" align="left" width="200px"/>
-Проект: aiotense
+Проект: tense
 <br>
 Ліцензія: Apache 2.0
 <br>
@@ -18,9 +18,9 @@ Typing: Annotated
     <br />
     <a href="https://github.com/othneildrew/Best-README-Template">Документація</a>
     ·
-    <a href="https://github.com/Animatea/aiotense/issues">Сповістити про баг</a>
+    <a href="https://github.com/Animatea/tense/issues">Сповістити про баг</a>
     ·
-    <a href="https://github.com/Animatea/aiotense/issues">Запропонувати ідею</a>
+    <a href="https://github.com/Animatea/tense/issues">Запропонувати ідею</a>
     </p>
 <div id="top"></div>
 <p align="center">
@@ -58,15 +58,16 @@ Typing: Annotated
     <li><a href="#ліцензия">Ліцензия</a></li>
     <li><a href="#контакт">Контакт</a></li>
     <li><a href="#подяки">Подяки</a></li>
+    <li><a href="#історія">Історія проекту</a></li>
   </ol>
 </details>
 
 ## Про проект
-<a href="https://circleci.com/gh/Animatea/aiotense/tree/main"><img height="20" src="https://circleci.com/gh/Animatea/aiotense/tree/main.svg?style=svg&circle-token=066eb74fc70db6eeaa6df5ade9e3d2df131b1de1"></a>
-<a href="https://pypi.org/project/aiotense/"><img height="20" alt="PyPi" src="https://img.shields.io/pypi/v/aiotense"></a>
+<a href="https://circleci.com/gh/Animatea/tense/tree/main"><img height="20" src="https://dl.circleci.com/status-badge/img/gh/Animatea/tense/tree/main.svg?style=svg"></a>
+<a href="https://pypi.org/project/tense/"><img height="20" alt="PyPi" src="https://img.shields.io/pypi/v/tense"></a>
 <a href="https://pypi.org/project/mypy/"><img height="20" alt="Mypy badge" src="http://www.mypy-lang.org/static/mypy_badge.svg"></a>
 <a href="https://github.com/psf/black"><img height="20" alt="Black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
-<a href="https://pycqa.github.io/isort/"><img alt="Supported python versions" height="20" src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&amp;labelColor=ef8336"/>
+<a href="https://pycqa.github.io/isort/"><img alt="Supported python versions" height="20" src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&amp;labelColor=ef8336"/></a>
 
 ### Ласкаво просимо
 > Чи потрібно Вам було конвертувати, наприклад, строку "1д1хвилина 2 сек" 
@@ -74,41 +75,36 @@ Typing: Annotated
 
 Ні? Тоді порадьте наш проект своїм друзям :) Якщо ви ще тут - йдемо далі!
 
-<h5 align="center">UML Діаграма з основною логікою проекта</h5>
-<img src="../assets/tense-uml.jpg" align="left"/>
-
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/повернутися на-початок-green?style=social&logo=github"></a></p>
 
 ## Перейти до використання
 ### PyPi
 ```bash
-$ pip3 install aiotense
+$ pip3 install tense
 ```
 
 ### Poetry
 ```bash
-$ poetry add aiotense
+$ poetry add tense
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/повернутися на-початок-green?style=social&logo=github"></a></p>
 
 ## Використання
 ### Основні можливості
+
 ```py
-import asyncio
 import datetime
-from aiotense import TenseParser
+from tense import TenseParser
 
 time_string = "1d2minutes 5 sec"
 
 # <-- Digit parser -->
 digit_parser = TenseParser(TenseParser.DIGIT)
-digit_value = asyncio.run(digit_parser.parse(time_string))
-# <-- Assertions -->
-assert digit_value == 86525
+assert digit_parser.parse(time_string) == 86525
 
 # <-- Timedelta parser -->
 delta_parser = TenseParser(TenseParser.TIMEDELTA)
-delta_value = asyncio.run(delta_parser.parse(time_string))
+delta_value = delta_parser.parse(time_string)
 # <-- Assertions -->
 assert isinstance(delta_value, datetime.timedelta)
 assert str(delta_value) == "1 day, 0:02:05"
@@ -116,9 +112,9 @@ assert str(delta_value) == "1 day, 0:02:05"
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/повернутися на-початок-green?style=social&logo=github"></a></p>
 
 ### Зміна наявних параметрів
+
 ```py
-import asyncio
-from aiotense import TenseParser, from_tense_file_source
+from tense import TenseParser, from_tense_file_source
 
 config_emulation = """
 [model.Tense]
@@ -132,18 +128,17 @@ aliases = хвилина, хвилин, хв
 """
 parser = TenseParser(
     TenseParser.TIMEDELTA,
-    config=from_tense_file_source(config_emulation),
+    tenses=from_tense_file_source(config_emulation),
 )
-delta_value = asyncio.run(parser.parse("1 хвилина 10хвилин 9  хв"))
-# <-- Assertions -->
-assert str(delta_value) ==  "1:20:00" # (кожна 120 * 2)
+delta_value = parser.parse("1 хвилина 10хвилин 9  хв")
+assert str(delta_value) == "1:20:00"  # (кожна 120 * 2)
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/повернутися на-початок-green?style=social&logo=github"></a></p>
 
 ### Додавання нових налаштувань
+
 ```py
-import asyncio
-from aiotense import TenseParser, from_tense_file_source
+from tense import TenseParser, from_tense_file_source
 
 config_emulation = """
 [model.Tense]  # Цей заголовок обов'язковий.
@@ -163,10 +158,9 @@ aliases = десятиліття, десятиліть
 
 parser = TenseParser(
     TenseParser.TIMEDELTA,
-    config=from_tense_file_source(config_emulation),
+    tenses=from_tense_file_source(config_emulation),
 )
-delta_value = asyncio.run(parser.parse("1рік 10 десятиліть5   секунд"))
-# <-- Assertions -->
+delta_value = parser.parse("1рік 10 десятиліть5   секунд")
 assert str(delta_value) == "36865 days, 0:00:05"
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/повернутися на-початок-green?style=social&logo=github"></a></p>
@@ -174,55 +168,55 @@ assert str(delta_value) == "36865 days, 0:00:05"
 ### FAQ
 Але що як нам потрібно парсити строку типу: "1day and 10 minutes + 5 seconds"?
 Давайте подивимось:
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser
+>> > from tense import TenseParser
 
->>> complex_string = "1day and 10 minutes + 5 seconds"
+>> > complex_string = "1day and 10 minutes + 5 seconds"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA)
->>> asyncio.run(parser.parse(complex_string))
+>> > parser = TenseParser(TenseParser.TIMEDELTA)
+>> > parser.parse(complex_string)
 '0:00:05'
 ```
 Зачекайте... Що? 5 секунд? Але ж там дні та хвилини...
-- Все окей, ви ж користуєтесь гнучким aiotense! Проблему можно вирішити двума шляхами:
+- Все окей, ви ж користуєтесь гнучким tense! Проблему можно вирішити двома шляхами:
   1) Ви пишете свій time_resolver
-  2) Ви вибираєте з вже існуючих aiotense.resolvers
+  2) Ви вибираєте з вже існуючих tense.resolvers
 
 Давайте продемонструю!
-Я буду використовувати другий варіант, так як існуючі резольвери мені підходять.
+Я буду використовувати інший варіант, так як існуючі резольвери мені підходять.
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, resolvers
+>> > from tense import TenseParser, resolvers
 
->>> complex_string = "1day and 10 minutes + 5 seconds"
+>> > complex_string = "1day and 10 minutes + 5 seconds"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
->>> asyncio.run(parser.parse(complex_string)) 
+>> > parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
+>> > parser.parse(complex_string)
 '1 day, 0:10:05'
 ```
 Ну, так вже краще!
 
-**aiotense.application.resolvers.smart_resolver()** також не є чутливим до регістру!
+**tense.application.resolvers.smart_resolver()** також не є чутливим до регістру!
+
 ```py
->>> import asyncio
->>> from aiotense import TenseParser, resolvers
+>> > from tense import TenseParser, resolvers
 
->>> complex_string = "1DAY and 10 MINUTES + 5 SECONDS"
+>> > complex_string = "1DAY and 10 MINUTES + 5 SECONDS"
 
->>> parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
->>> asyncio.run(parser.parse(complex_string)) 
+>> > parser = TenseParser(TenseParser.TIMEDELTA, time_resolver=resolvers.smart_resolver)
+>> > parser.parse(complex_string)
 '1 day, 0:10:05'
 ```
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/повернутися на-початок-green?style=social&logo=github"></a></p>
 
 ## Приклади
-Якщо ви гадаєте, що на цьмоу можливості aiotense закінчуються, то ви помиляєтесь!
-Можливостей aiotense забагато для README, тому я пропоную Вам продовжити перегляд 
+Якщо ви гадаєте, що на цьмоу можливості tense закінчуються, то ви помиляєтесь!
+Можливостей tense забагато для README, тому я пропоную Вам продовжити перегляд 
 прикладів використання тут:
 <p align="center">
 <br />
-<a href="https://github.com/Animatea/aiotense/tree/main/examples">Aiotense Приклади</a>
+<a href="https://github.com/Animatea/tense/tree/main/examples">Tense Приклади</a>
 </p>
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/повернутися на-початок-green?style=social&logo=github"></a></p>
 
@@ -242,7 +236,7 @@ assert str(delta_value) == "36865 days, 0:00:05"
 <!-- LICENSE -->
 ## Ліцензия
 
-Поширюється під ліцензією Apache 2.0. Див. детальніше [`LICENSE`](https://github.com/Animatea/aiotense/blob/main/LICENSE).
+Поширюється під ліцензією Apache 2.0. Див. детальніше [`LICENSE`](https://github.com/Animatea/tense/blob/main/LICENSE).
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/повернутися на-початок-green?style=social&logo=github"></a></p>
 
@@ -266,3 +260,17 @@ assert str(delta_value) == "36865 days, 0:00:05"
 * [MkDocs Material](https://squidfunk.github.io/mkdocs-material/)
 
 <p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/повернутися на-початок-green?style=social&logo=github"></a></p>
+
+## Історія
+Спочатку проект був зроблений асинхронним, що суттєво уповільнювало процес парсингу, адже tense є CPU-bound модулем.
+
+Відрефакторивши проект, ми отримали `~x22.31` прискорення в обробці складних рядків (за допомогою `smart_resolver`).
+> Було: `~0.00095030...`μs | Стало: `~0.00004260...`μs
+
+І `~x38.28` прискорення в обробці простих рядків, відповідно (за допомогою `basic_resolver`).
+> Було: `~0.00062400...`μs | Стало: `~0.00001630...`μs
+
+Попередня (асинхронна) версія, як і раніше, доступна у гілках репозиторію - https://github.com/Animatea/tense/tree/async-final,
+але сайт із документацією буде змінено.
+
+<p align="right"><a href="#top"><img height="20" src="https://img.shields.io/badge/повернутися на-початок-green?style=social&logo=github"></a></p>
diff --git a/noxfile.py b/noxfile.py
new file mode 100644
index 0000000..cecce47
--- /dev/null
+++ b/noxfile.py
@@ -0,0 +1,37 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
+from typing import Final
+
+import nox
+
+MAIN_PKG: Final[str] = "tense"
+TESTS_PKG: Final[str] = "tests"
+EXAMPLES_PKG: Final[str] = "examples"
+
+NOX_PKGS: Final[tuple[str, ...]] = (MAIN_PKG, TESTS_PKG, EXAMPLES_PKG)
+
+DEV_REQUIREMENTS: Final[tuple[str, ...]] = ("-r", "dev-requirements.txt")
+
+
+@nox.session(python=["3.9", "3.10"])
+def pytest(session: nox.Session) -> None:
+    session.install(*DEV_REQUIREMENTS)
+    session.run("pytest")
+
+
+@nox.session
+def reformat_code(session: nox.Session) -> None:
+    session.install(*DEV_REQUIREMENTS)
+    session.run("black", "--config=pyproject.toml", *NOX_PKGS)
+    session.run("isort", "--profile=black", *NOX_PKGS)
diff --git a/pyproject.toml b/pyproject.toml
index b396da4..e5a6934 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,5 @@
 [tool.mypy]
-mypy_path = "aiotense"
+mypy_path = "tense"
 python_version = "3.10"
 exclude = "__init__.py"
 
@@ -17,28 +17,27 @@ no_site_packages = true
 [tool.isort]
 profile = "black"
 py_version = 310
-src_paths = ["aiotense", "tests"]
+src_paths = ["tense", "tests", "examples"]
 
 [tool.black]
 target-version = ["py310"]
 include = '\.pyi?$'
 
 [tool.poetry]
-name = "aiotense"
-version = "0.0.3a"
+name = "tense"
+version = "1.0.0b"
 description = "Time Processing Tool"
 authors = ["Animatea <animatea.programming@gmail.com>"]
 readme = "PYPI_README.md"
 license = "Apache-2.0"
-homepage = "https://github.com/Animatea/aiotense"
-repository = "https://github.com/Animatea/aiotense"
-documentation = "https://animatea.github.io/aiotense/"
+homepage = "https://github.com/Animatea/tense"
+repository = "https://github.com/Animatea/tense"
+documentation = "https://animatea.github.io/tense/"
 include = [
     "LICENSE", "assets", "i18n",
 ]
-keywords = ["parser", "tool", "aiotense", "time", "utility"]
+keywords = ["parser", "tool", "tense", "time", "utility"]
 classifiers = [
-    "Programming Language :: Python :: 3.9",
     "Programming Language :: Python :: 3.10",
     "Development Status :: 3 - Alpha",
     "Topic :: Software Development :: Libraries :: Python Modules",
@@ -51,24 +50,24 @@ classifiers = [
     "Topic :: Utilities",
 ]
 packages = [
-    { include = "aiotense" },
+    { include = "tense" },
 ]
 
 [tool.poetry.urls]
-"Bug Tracker" = "https://github.com/Animatea/aiotense/issues"
-"Documentation" = "https://animatea.github.io/aiotense/"
+"Bug Tracker" = "https://github.com/Animatea/tense/issues"
+"Documentation" = "https://animatea.github.io/tense/"
 
 [tool.poetry.dependencies]
 python = "^3.9"
 
 [tool.poetry.dev-dependencies]
-parse = "^1.19.0"
-PyHamcrest = "2^.0.3"
-pytest = "^7.1.2"
-behave = "^1.2.6"
-black = "^22.3.0"
-isort = "^5.10.1"
-mypy = "^0.961"
+parse = "1.19.0"
+PyHamcrest = "2.0.3"
+pytest = "7.1.2"
+behave = "1.2.6"
+black = "22.3.0"
+isort = "5.10.1"
+mypy = "0.961"
 
 [build-system]
 requires = ["poetry>=0.12"]
diff --git a/aiotense/__init__.py b/tense/__init__.py
similarity index 76%
rename from aiotense/__init__.py
rename to tense/__init__.py
index b4b7ae7..87efd78 100644
--- a/aiotense/__init__.py
+++ b/tense/__init__.py
@@ -12,10 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 """Root package."""
-from aiotense.application import TenseParser, resolvers
-from aiotense.domain import model, units
-from aiotense.service_layer import unit_of_work
-from aiotense.service_layer.dot_tense import from_tense_file, from_tense_file_source
+from tense.application import TenseParser, resolvers
+from tense.domain import model, units
+from tense.service_layer import unit_of_work
+from tense.service_layer.dot_tense import from_tense_file, from_tense_file_source
 
 __all__ = [
     "model",
diff --git a/aiotense/adapters/__init__.py b/tense/adapters/__init__.py
similarity index 100%
rename from aiotense/adapters/__init__.py
rename to tense/adapters/__init__.py
diff --git a/aiotense/adapters/converters.py b/tense/adapters/converters.py
similarity index 85%
rename from aiotense/adapters/converters.py
rename to tense/adapters/converters.py
index 45f5916..13e5de2 100644
--- a/aiotense/adapters/converters.py
+++ b/tense/adapters/converters.py
@@ -11,17 +11,17 @@
 # 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.
-"""Adapters of aiotense.application.ports."""
+"""Adapters of tense.application.ports."""
 __all__ = ["TimedeltaConverter"]
 
 from datetime import timedelta
 from typing import Any
 
-from aiotense.application.ports import converters
+from tense.application.ports import converters
 
 
 class TimedeltaConverter(converters.AbstractConverter[timedelta]):
-    async def convert(self, value: Any) -> timedelta:
+    def convert(self, value: Any) -> timedelta:
         if not isinstance(value, (int, float)):
             raise ValueError("Value must be instance of (int, float).")
 
diff --git a/aiotense/adapters/parsers.py b/tense/adapters/parsers.py
similarity index 55%
rename from aiotense/adapters/parsers.py
rename to tense/adapters/parsers.py
index 01ea5d2..9b68c57 100644
--- a/aiotense/adapters/parsers.py
+++ b/tense/adapters/parsers.py
@@ -11,37 +11,40 @@
 # 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.
-"""Adapters of aiotense.application.ports."""
+"""Adapters of tense.application.ports."""
 from __future__ import annotations
 
 __all__ = ["DigitParser", "TimedeltaParser"]
 
 from typing import TYPE_CHECKING, Callable, Optional
 
-from aiotense.application.ports import parsers
+from tense.application.ports import parsers as abc_parsers
 
 from . import converters
 
 if TYPE_CHECKING:
-    from aiotense.application.ports import converters as abc_converters
-    from aiotense.domain import model
+    from tense.application.ports import converters as abc_converters
+    from tense.domain import model
 
 
-class DigitParser(parsers.AbstractParser):
-    async def _parse(self, raw_str: str) -> int:
-        multiplier = self.tense.multiplier
-        resolved = self._resolver(raw_str, self.tense)
+class DigitParser(abc_parsers.AbstractParser):
+    def _parse(self, raw_str: str, /) -> int:
+        resolved = self._resolver(raw_str, self._tense)
         duration = 0
-
-        for pos, word in enumerate(resolved):
-            for unit in self.tense:
-                if word in unit.aliases:
-                    prev_entry = resolved[pos - 1]
-                    if not prev_entry.isdigit():
-                        continue
-
-                    duration += int(prev_entry) * (unit.duration * multiplier)
-
+        last_num: Optional[int] = None
+        try:
+            while 1:
+                part = next(resolved)
+                if part.isdigit():
+                    last_num = int(part)
+                    continue
+                if last_num is None:
+                    continue
+                for unit in self._iterunits:
+                    if part in unit.aliases:
+                        duration += last_num * (unit.duration * self._tense.multiplier)
+        except StopIteration:
+            return duration
         return duration
 
 
@@ -51,10 +54,12 @@ def __init__(
         *,
         tense: model.Tense,
         resolver: Optional[Callable[[str, model.Tense], list[str]]] = None,
+        iteration_speedup: bool = False,
         converter: Optional[abc_converters.AbstractConverter] = None,
     ) -> None:
         super().__init__(
             tense=tense,
             resolver=resolver,
+            iteration_speedup=iteration_speedup,
             converter=converters.TimedeltaConverter(),
         )
diff --git a/aiotense/adapters/repository.py b/tense/adapters/repository.py
similarity index 71%
rename from aiotense/adapters/repository.py
rename to tense/adapters/repository.py
index 925d906..8f2bd59 100644
--- a/aiotense/adapters/repository.py
+++ b/tense/adapters/repository.py
@@ -11,7 +11,7 @@
 # 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.
-"""Adapters of aiotense.application.ports."""
+"""Adapters of tense.application.ports."""
 from __future__ import annotations
 
 __all__ = ["TenseRepository"]
@@ -20,11 +20,11 @@
 import dataclasses
 from typing import Any, Optional, Type
 
-from aiotense.application.ports.repository import AbstractTenseRepository
-from aiotense.domain import units
+from tense.application.ports import repository as abc_repository
+from tense.domain import units
 
 
-class TenseRepository(AbstractTenseRepository):
+class TenseRepository(abc_repository.AbstractTenseRepository):
     # <inherited docstring from :class:`TenseRepository`> #
     _tense_config: dict[str, Any] = {
         "model.Tense": {
@@ -86,29 +86,32 @@ class TenseRepository(AbstractTenseRepository):
     }
 
     def __init__(self, config: Optional[dict[str, Any]] = None, /) -> None:
-        if config is None:
-            config = self._tense_config
-        super().__init__(config)
+        cfg = copy.copy(self._tense_config)
+        if config is not None:
+            cfg.update(config)
+        super().__init__(cfg)
 
     def get_config(self) -> dict[str, Any]:
+        # <inherited docstring from :class:`AbstractTenseRepository`> #
         return copy.deepcopy(self._config)
 
-    def get_setting(self, path: str, setting: str, /) -> Any:
-        return self._config[path][setting]
+    def get_setting(self, group: str, setting: str, /) -> Any:
+        # <inherited docstring from :class:`AbstractTenseRepository`> #
+        return self._config[group][setting]
 
-    def add_virtual_unit(self, unit: units.VirtualUnit) -> TenseRepository:
-        # <inherited docstring from :class:`TenseRepository`> #
+    def add_virtual_unit(self, unit: units.VirtualUnit, /) -> TenseRepository:
+        # <inherited docstring from :class:`AbstractTenseRepository`> #
         self._config["model.Tense"]["virtual"].append(dataclasses.asdict(unit))
         return self
 
-    def add_virtual_unit_dict(self, unit_dict: dict[str, Any]) -> TenseRepository:
-        # <inherited docstring from :class:`TenseRepository`> #
+    def add_virtual_unit_dict(self, unit_dict: dict[str, Any], /) -> TenseRepository:
+        # <inherited docstring from :class:`AbstractTenseRepository`> #
         self._config["model.Tense"]["virtual"].append(unit_dict)
         return self
 
     def add_aliases_to(
-        self, unit: Type[units.Unit], aliases: list[str]
+        self, unit: Type[units.Unit], /, aliases: list[str]
     ) -> TenseRepository:
-        # <inherited docstring from :class:`TenseRepository`> #
+        # <inherited docstring from :class:`AbstractTenseRepository`> #
         self._config["units." + unit.__name__]["aliases"].extend(aliases)
         return self
diff --git a/aiotense/application/__init__.py b/tense/application/__init__.py
similarity index 100%
rename from aiotense/application/__init__.py
rename to tense/application/__init__.py
diff --git a/aiotense/application/exceptions.py b/tense/application/exceptions.py
similarity index 100%
rename from aiotense/application/exceptions.py
rename to tense/application/exceptions.py
diff --git a/tense/application/factory.py b/tense/application/factory.py
new file mode 100644
index 0000000..2ae2cb7
--- /dev/null
+++ b/tense/application/factory.py
@@ -0,0 +1,123 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
+"""Tense factory."""
+from __future__ import annotations
+
+__all__ = ["TenseParser"]
+
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    Iterator,
+    Optional,
+    Type,
+    Union,
+    overload,
+)
+
+from tense.adapters import parsers, repository
+from tense.domain import model
+
+if TYPE_CHECKING:
+    from .ports import converters as abc_converters
+    from .ports import repository as abc_repository
+
+from . import exceptions, resolvers
+from .ports import parsers as abc_parsers
+
+
+class TenseParser:
+    """Base parsers factory.
+
+    Parameters:
+    -----------
+    parser_cls: :class:`Type[abc_parsers.AbstractParser]` = parsers.DigitParser
+        Concrete parser type.
+    tenses: :class:`Optional[abc_repository.AbstractTenseRepository]` = None, *
+        Configuration repository for time parser.
+    converter: :class:`Optional[abc_converters.AbstractConverter]` = None, *
+        Value converter.
+    time_resolver: :class:`Optional[Callable[[str, model.Tense], Iterator[str]]]` = None, *
+        Raw string resolver.
+    iteration_speedup: :class:`bool` = False, *
+        If True, then part of the logic will be moved to the `__init__` of the class,
+        which will slightly slow down the creation of an instance of the class, but
+        speed up the parsing process.
+
+    Raises:
+    -------
+    :class:`exceptions.InvalidParserType`
+        Raises if 'parser_cls' is not subclass of :class:`abc_parsers.AbstractParser`
+    """
+
+    __slots__ = ()
+
+    TIMEDELTA = parsers.TimedeltaParser
+    DIGIT = parsers.DigitParser
+
+    @overload
+    def __new__(cls) -> abc_parsers.AbstractParser:
+        ...
+
+    @overload
+    def __new__(
+        cls,
+        parser_cls: Type[abc_parsers.AbstractParser],
+    ) -> abc_parsers.AbstractParser:
+        ...
+
+    @overload
+    def __new__(
+        cls,
+        parser_cls: Type[abc_parsers.AbstractParser],
+        *,
+        tenses: Optional[abc_repository.AbstractTenseRepository],
+        converter: Optional[abc_converters.AbstractConverter],
+        time_resolver: Optional[Callable[[str, model.Tense], Iterator[str]]],
+        iteration_speedup: bool,
+    ) -> abc_parsers.AbstractParser:
+        ...
+
+    def __new__(
+        cls,
+        parser_cls: Type[abc_parsers.AbstractParser] = DIGIT,
+        *,
+        tenses: Optional[
+            Union[abc_repository.AbstractTenseRepository, dict[str, Any]]
+        ] = None,
+        converter: Optional[abc_converters.AbstractConverter] = None,
+        time_resolver: Optional[Callable[[str, model.Tense], Iterator[str]]] = None,
+        iteration_speedup: bool = False,
+    ) -> abc_parsers.AbstractParser:
+        tense_repository_cfg = repository.TenseRepository().config
+        if tenses is not None:
+            tenses = tenses if isinstance(tenses, dict) else tenses.config
+            tense_repository_cfg.update(tenses)
+
+        if time_resolver is None:
+            time_resolver = resolvers.basic_resolver
+
+        if not issubclass(parser_cls, abc_parsers.AbstractParser):
+            raise exceptions.InvalidParserType(
+                f"Invalid parser type, you can only use {abc_parsers.AbstractParser.__subclasses__()}."
+            )
+        instance = parser_cls.__new__(parser_cls)  # type: ignore[call-overload]
+        instance.__init__(
+            tense=model.Tense.from_dict(tense_repository_cfg),
+            resolver=time_resolver,
+            converter=converter,
+            iteration_speedup=iteration_speedup,
+        )
+        return instance
diff --git a/aiotense/application/ports/converters.py b/tense/application/ports/converters.py
similarity index 65%
rename from aiotense/application/ports/converters.py
rename to tense/application/ports/converters.py
index fd42aa3..3ae6a27 100644
--- a/aiotense/application/ports/converters.py
+++ b/tense/application/ports/converters.py
@@ -11,7 +11,7 @@
 # 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.
-"""Interfaces that are implemented in aiotense.adapters."""
+"""Interfaces that are implemented in tense.adapters."""
 __all__ = ["AbstractConverter"]
 
 import abc
@@ -21,6 +21,20 @@
 
 
 class AbstractConverter(abc.ABC, Generic[T_co]):
+    """Interface for converters.
+    A generic that accepts a type to which a specific value
+    will be converted.
+    """
+
+    __slots__ = ()
+
     @abc.abstractmethod
-    async def convert(self, value: Any) -> T_co:
+    def convert(self, value: Any, /) -> T_co:
+        """Converts value to `T_co` type.
+
+        Parameters:
+        -----------
+        value: :class:`Any`, /
+            Value to convert.
+        """
         ...
diff --git a/tense/application/ports/parsers.py b/tense/application/ports/parsers.py
new file mode 100644
index 0000000..65a0ca6
--- /dev/null
+++ b/tense/application/ports/parsers.py
@@ -0,0 +1,111 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
+"""Interfaces that are implemented in tense.adapters."""
+from __future__ import annotations
+
+__all__ = ["AbstractParser"]
+
+import abc
+from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, final
+
+from tense.application.ports import converters
+
+if TYPE_CHECKING:
+    from tense.domain import model
+
+
+class AbstractParser(abc.ABC):
+    """Interface for basic parsers.
+
+    Parameters:
+    -----------
+    tense: :class:`model.Tense`, *
+        Configuration repository for time parser.
+    resolver: :class:`Optional[Callable[[str, model.Tense], Iterator[str]]]` = None, *
+        Raw string resolver.
+    converter: :class:`Optional[converters.AbstractConverter]` = None, *
+        Value converter.
+    iteration_speedup: :class:`bool` = False, *
+        If True, then part of the logic will be moved to the `__init__` of the class,
+        which will slightly slow down the creation of an instance of the class, but
+        speed up the parsing process.
+    """
+
+    def __init__(
+        self,
+        *,
+        tense: model.Tense,
+        resolver: Optional[Callable[[str, model.Tense], Iterator[str]]] = None,
+        converter: Optional[converters.AbstractConverter] = None,
+        iteration_speedup: bool = False,
+    ) -> None:
+        self._tense = tense
+        self._converter = converter
+        self._resolver = resolver
+        self._iterunits = set(tense) if iteration_speedup else tense
+
+    @final
+    def parse(self, raw_str: str, /) -> Any:
+        """Base method that calls abstract ._parse().
+        If the converter is not equal to None, then the value
+        will be converted.
+
+        !!! note
+            This method is final and cannot be overridden.
+
+        Parameters:
+        -----------
+        raw_str: :class:`str`, /
+            Raw string to parse
+        """
+        value = self._parse(raw_str)
+        if self._converter is not None:
+            value = self._converter.convert(value)
+
+        return value
+
+    @abc.abstractmethod
+    def _parse(self, raw_str: str, /) -> Any:
+        """Abstract method that calls in .parse().
+        Methods are implemented this way for hooks (converters, etc).
+
+        Parameters:
+        -----------
+        raw_str: :class:`str`, /
+            Raw string to parse.
+        """
+        ...
+
+    @property
+    def resolver(self) -> Optional[Callable[[str, model.Tense], Iterator[str]]]:
+        """Data descriptor that returns current resolver."""
+        return self._resolver
+
+    @resolver.setter
+    def resolver(
+        self, new_resolver: Callable[[str, model.Tense], Iterator[str]], /
+    ) -> None:
+        """Sets new resolver.
+
+        When sets a new resolver, make sure it matches the base signature of
+        all resolvers (2 arguments, the first one takes a string, the second
+        one takes a model.Tense object).
+        Otherwise, parsers may work incorrectly, or even give an unexpected error.
+
+        !!! dunger
+            new_resolver must be callable.
+        """
+        if not callable(new_resolver):
+            raise ValueError("Resolver must be callable.")
+        self._resolver = new_resolver
diff --git a/tense/application/ports/repository.py b/tense/application/ports/repository.py
new file mode 100644
index 0000000..b051930
--- /dev/null
+++ b/tense/application/ports/repository.py
@@ -0,0 +1,121 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
+"""Interfaces that are implemented in tense.adapters."""
+from __future__ import annotations
+
+__all__ = ["AbstractTenseRepository"]
+
+import abc
+from typing import TYPE_CHECKING, Any, Optional, Type, TypedDict
+
+if TYPE_CHECKING:
+    from tense.domain import units
+
+    class _VirtualUnitDict(TypedDict):
+        duration: int
+        aliases: list[str]
+
+
+class AbstractTenseRepository(abc.ABC):
+    """Interface for tense repository.
+
+    Repositories are a kind of wrapper for get / add operations on
+    a specific collection. It is worth noting that atomic operations
+    (such as update/delete/replace) for changing repository data are
+    implemented using the UnitOfWork pattern in the service layer.
+
+    This repository is a wrapper over the global settings for parsers
+    and aliases for time units.
+
+    !!! info
+        `Add` operations allows fluent-style.
+
+    Parameters:
+    -----------
+    config: :class:`Optional[dict[str, Any]]` = None, /
+        Repository configuration.
+    """
+
+    def __init__(self, config: Optional[dict[str, Any]] = None, /) -> None:
+        self._config = config
+
+    @property
+    def config(self) -> Optional[dict[str, Any]]:
+        """Non-data descriptor that returns repository configuration."""
+        return self._config
+
+    @abc.abstractmethod
+    def get_config(self) -> dict[str, Any]:
+        """Returns deepcopy of repository configuration."""
+        ...
+
+    @abc.abstractmethod
+    def get_setting(self, group: str, setting: str, /) -> Any:
+        """Returns setting value.
+
+        Parameters:
+        -----------
+        group: :class:`str`, /
+            This argument is the name of the group whose settings you want to get.
+            Headers in configurations like "model.Tense", "units.Second"... are
+            considered as a group.
+        setting: :class:`str`, /
+            Group setting name.
+        """
+        ...
+
+    @abc.abstractmethod
+    def add_virtual_unit(self, unit: units.VirtualUnit, /) -> AbstractTenseRepository:
+        """Adds custom unit of time.
+
+        Parameters:
+        -----------
+        unit: :class:`units.VirtualUnit`, /
+            Virtual unit object.
+        """
+        ...
+
+    @abc.abstractmethod
+    def add_virtual_unit_dict(
+        self, unit_dict: _VirtualUnitDict, /
+    ) -> AbstractTenseRepository:
+        """Adds custom unit of time.
+
+        Alternative method for adding virtual unit to repository.
+        Is faster than adding using the units.VirtualUnit object.
+
+        Parameters:
+        -----------
+        unit_dict: :class:`_VirtualUnitDict`
+            VirtualUnit dictionary.
+        """
+        ...
+
+    @abc.abstractmethod
+    def add_aliases_to(
+        self,
+        unit: Type[units.Unit],
+        /,
+        aliases: list[str],
+    ) -> AbstractTenseRepository:
+        """Adds aliases to unit of time.
+
+        Parameters:
+        -----------
+        unit: :class:`Type[units.Unit]`, /
+            Concrete unit of time.
+        aliases: :class:`list[str]`
+            List of aliases to add.
+        """
+        ...
diff --git a/tense/application/resolvers.py b/tense/application/resolvers.py
new file mode 100644
index 0000000..794275c
--- /dev/null
+++ b/tense/application/resolvers.py
@@ -0,0 +1,104 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
+"""Time resolvers.
+Resolvers have a basic signature that they must match. Abstract signature
+consists of two constant parameters: the first one takes a string to parse,
+the second one - a `model.Tense` object as a container for aliases.
+
+It's worth noting that resolvers must return a string iterator.
+
+How can I create my own resolver? You can see examples in:
+    - https://github.com/Animatea/aiotense/tree/main/examples
+"""
+from __future__ import annotations
+
+__all__ = [
+    "basic_resolver",
+    "smart_resolver",
+]
+
+import re
+from typing import TYPE_CHECKING, Iterator
+
+if TYPE_CHECKING:
+    from tense.domain import model
+
+DIGIT_PATTERN: re.Pattern[str] = re.compile(r"(\d+)")
+
+
+def basic_resolver(raw_str: str, _: model.Tense) -> Iterator[str]:
+    """Resolves simple strings.
+
+    !!! note
+        _: :class:`model.Tense` parameter is not used here, but is required to
+        match with the resolver signature.
+
+    Examples:
+    ---------
+    >>> from tense import model, resolvers
+    >>> from tense.adapters import repository
+
+    >>> tense = model.Tense.from_repository(repository.TenseRepository())
+    >>> list(resolvers.basic_resolver("1d1min", tense))
+    ['1', 'd', '1', 'min']
+    >>> list(resolvers.basic_resolver("1d 1min", tense))
+    ['1', 'd', '1', 'min']
+    >>> list(resolvers.basic_resolver("1d1min 2 seconds", tense))
+    ['1', 'd', '1', 'min', '2', 'seconds']
+    """
+    return filter(
+        bool,
+        DIGIT_PATTERN.split(raw_str.replace(" ", "")),
+    )
+
+
+def smart_resolver(raw_str: str, tense: model.Tense) -> Iterator[str]:
+    """Resolves complex strings.
+
+    !!! note
+        Single letter aliases are not supported.
+
+    Examples (extends basic_resolver()):
+    --------------------
+    >>> from tense import model, resolvers
+    >>> from tense.adapters import repository
+
+    >>> tense = model.Tense.from_repository(repository.TenseRepository())
+    >>> list(resolvers.smart_resolver("1year and 10 minutes + 5 seconds", tense))
+    ['1', 'year', '10', 'min', '5', 'sec']
+    """
+    basic_resolve = basic_resolver(raw_str, tense)
+    if any(not p.isalpha() for p in basic_resolve if not p.isdigit()):
+        # Removes any char of string.punctuation
+        def _resolve_p(p: str, /) -> str:
+            return p if p.isdigit() else "".join(filter(str.isalpha, p))
+
+        basic_resolve = (_resolve_p(p) for p in basic_resolver(raw_str, tense))
+    else:
+        basic_resolve = basic_resolver(raw_str, tense)
+
+    unit_aliases: list[str] = sum((u.aliases for u in tense), [])
+    for part in basic_resolve:
+        if part.isdigit():
+            yield part
+            continue
+
+        part = part.lower()  # case insensitive
+        for alias in unit_aliases:
+            if alias in part:
+                if len(alias) == 1 and len(part) > 1:
+                    # For 'd', 's', 'y', ... aliases.
+                    continue
+                yield alias
+                break
diff --git a/tense/domain/model.py b/tense/domain/model.py
new file mode 100644
index 0000000..2e07afb
--- /dev/null
+++ b/tense/domain/model.py
@@ -0,0 +1,155 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
+"""Tense domain."""
+from __future__ import annotations
+
+__all__ = ["Tense"]
+
+import warnings
+from dataclasses import dataclass, field
+from typing import TYPE_CHECKING, Any, Iterator
+
+from tense.domain import units
+
+if TYPE_CHECKING:
+    from tense.application.ports import repository
+
+
+@dataclass
+class Tense:
+    """A class that is responsible for aliasing units of time.
+
+    !!! warning
+        If the `multiplier` attribute of this class is less than
+        or equal to zero, a warning will be issued to the console,
+        since in this case the parsers may not work correctly.
+
+    !!! note
+        This class adds each virtual unit of time to `self.__dict__`,
+        as it filters values from there in `__iter__` ,method.
+
+        For each virtual unit, a name is generated according to the
+        following pattern: `virtual_name` + `virtual_number`.
+
+    !!! info
+        This class caches time units in the `_cached_units` attribute
+        when iterating.
+
+    Parameters:
+    -----------
+    second: :class:`units.Second`
+        Representing the `second` as a unit of time.
+    minute: :class:`units.Minute`
+        Representing the `minute` as a unit of time.
+    hour: :class:`units.Hour`
+        Representing the `hour` as a unit of time.
+    day: :class:`units.Day`
+        Representing the `day` as a unit of time.
+    week: :class:`units.Week`
+        Representing the `week` as a unit of time.
+    year: :class:`units.Year`
+        Representing the `year` as a unit of time.
+    virtual: :class:`list[dict[str, Any]]` = field(default_factory=list)
+        List of custom units of time.
+    """
+
+    second: units.Second
+    minute: units.Minute
+    hour: units.Hour
+    day: units.Day
+    week: units.Week
+    year: units.Year
+    multiplier: int = 1
+    virtual: list[dict[str, Any]] = field(default_factory=list)
+
+    def __post_init__(self) -> None:
+        self._cached_units: set[units.Unit] = set()
+        if self.multiplier <= 0:
+            warnings.warn(
+                "The time multiplier is less than zero, the work of "
+                "parsers may be incorrect. It is recommended to set "
+                "the value more than zero."
+            )
+        if self.virtual:
+            self._resolve_virtual()
+
+    def __iter__(self) -> Iterator[units.Unit]:
+        _cached_units = self._cached_units
+        if _cached_units:
+            yield from _cached_units
+        else:
+            for unit in self.__dict__.values():
+                if isinstance(unit, units.Unit):
+                    self._cached_units.add(unit)
+                    yield unit
+
+    def _resolve_virtual(self) -> None:
+        """Adds virtual units to the current state of the class."""
+        for n, unit_dict in enumerate(self.virtual):
+            self.__dict__[f"virtual{n}"] = units.VirtualUnit(**unit_dict)
+
+    @classmethod
+    def from_dict(cls, tense_dict: dict[str, Any], /) -> Tense:
+        """An alternative way to create an instance of a class using
+        a configuration dictionary.
+
+        !!! note
+            This constructor interacts with the globals() of this module.
+            Therefore, when adding new settings that are associated with
+            other modules, these modules will need to be imported into this
+            one, otherwise they simply will not be found.
+
+        Parameters:
+        -----------
+        tense_dict: :class:`dict[str, Any]`, /
+            Configuration dictionary.
+        """
+        tense_attrs = {}
+        for key, attrs in tense_dict.items():
+            module, cls_name = key.split(".")
+            if cls_name == cls.__name__:
+                # This is the main section with general configuration settings.
+                if not isinstance(attrs, dict):
+                    # If it's empty.
+                    continue
+
+                tense_attrs.update(attrs)
+                continue
+
+            module = globals()[module]
+            unit_cls = getattr(module, cls_name)
+            tense_attrs[cls_name.lower()] = unit_cls(**attrs)
+            continue
+
+        return cls(**tense_attrs)
+
+    @classmethod
+    def from_repository(
+        cls,
+        repo: repository.AbstractTenseRepository,
+        /,
+    ) -> Tense:
+        """An alternative way to create an instance of a class using a tense repository.
+        Works at a higher level of abstraction than `cls.from_dict`.
+
+        Parameters:
+        -----------
+        repo: :class:`repository.AbstractTenseRepository`, /
+            Tense repository.
+        """
+        return cls.from_dict(repo.config)
+
+    @property
+    def all(self) -> list[str]:
+        return sum(u.aliases for u in self)
diff --git a/tense/domain/units.py b/tense/domain/units.py
new file mode 100644
index 0000000..e2d725e
--- /dev/null
+++ b/tense/domain/units.py
@@ -0,0 +1,226 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
+"""Tense domain."""
+from __future__ import annotations
+
+__all__ = ["Unit", "Minute", "Hour", "Day", "Week", "VirtualUnit", "Second"]
+
+import warnings
+from dataclasses import dataclass
+
+
+@dataclass
+class Unit:
+    """Base class for all units of time.
+
+    !!! info
+        This dataclass implements `__hash__` method,
+        if you want a subclass to inherit it, then
+        specify `eq=False` in @dataclass decorator.
+
+    !!! warning
+        If the `duration` attribute of this class is
+        less than or equal to zero, then a warning will
+        be generated to the console, since in this case
+        the parsers may not work correctly.
+
+    Parameters:
+    -----------
+    aliases: :class:`list[str]`
+        Aliases of unit of time.
+    duration: :class:`int`
+        Unit of time duration.
+    """
+
+    aliases: list[str]
+    duration: int
+
+    def __post_init__(self) -> None:
+        if self.duration <= 0:
+            warnings.warn(
+                "The unit duration is less than zero, the work of "
+                "parsers may be incorrect. It is recommended to set "
+                "the value more than zero."
+            )
+
+    def __hash__(self) -> int:
+        return self.duration
+
+
+@dataclass(eq=False)
+class VirtualUnit(Unit):
+    """Used to create custom units of time.
+
+    !!! info
+        This subclass inherits the `__hash__` method.
+
+    !!! warning
+        If the `duration` attribute of this class is
+        less than or equal to zero, then a warning will
+        be generated to the console, since in this case
+        the parsers may not work correctly.
+
+    Parameters:
+    -----------
+    aliases: :class:`list[str]`
+        Aliases of unit of time.
+    duration: :class:`int`
+        Unit of time duration.
+    """
+
+    pass
+
+
+@dataclass(eq=False)
+class Second(Unit):
+    """Used to create custom units of time.
+
+    !!! info
+        This subclass inherits the `__hash__` method.
+
+    !!! warning
+        If the `duration` attribute of this class is
+        less than or equal to zero, then a warning will
+        be generated to the console, since in this case
+        the parsers may not work correctly.
+
+    Parameters:
+    -----------
+    aliases: :class:`list[str]`
+        Aliases of unit of time.
+    duration: :class:`int` = 1
+        Unit of time duration.
+    """
+
+    duration: int = 1
+
+
+@dataclass(eq=False)
+class Minute(Unit):
+    """Used to create custom units of time.
+
+    !!! info
+        This subclass inherits the `__hash__` method.
+
+    !!! warning
+        If the `duration` attribute of this class is
+        less than or equal to zero, then a warning will
+        be generated to the console, since in this case
+        the parsers may not work correctly.
+
+    Parameters:
+    -----------
+    aliases: :class:`list[str]`
+        Aliases of unit of time.
+    duration: :class:`int` = 60
+        Unit of time duration.
+    """
+
+    duration: int = 60
+
+
+@dataclass(eq=False)
+class Hour(Unit):
+    """Used to create custom units of time.
+
+    !!! info
+        This subclass inherits the `__hash__` method.
+
+    !!! warning
+        If the `duration` attribute of this class is
+        less than or equal to zero, then a warning will
+        be generated to the console, since in this case
+        the parsers may not work correctly.
+
+    Parameters:
+    -----------
+    aliases: :class:`list[str]`
+        Aliases of unit of time.
+    duration: :class:`int` = 60 * 60
+        Unit of time duration.
+    """
+
+    duration: int = 60 * 60
+
+
+@dataclass(eq=False)
+class Day(Unit):
+    """Used to create custom units of time.
+
+    !!! info
+        This subclass inherits the `__hash__` method.
+
+    !!! warning
+        If the `duration` attribute of this class is
+        less than or equal to zero, then a warning will
+        be generated to the console, since in this case
+        the parsers may not work correctly.
+
+    Parameters:
+    -----------
+    aliases: :class:`list[str]`
+        Aliases of unit of time.
+    duration: :class:`int` = 60 * 60 * 24
+        Unit of time duration.
+    """
+
+    duration: int = 60 * 60 * 24
+
+
+@dataclass(eq=False)
+class Week(Unit):
+    """Used to create custom units of time.
+
+    !!! info
+        This subclass inherits the `__hash__` method.
+
+    !!! warning
+        If the `duration` attribute of this class is
+        less than or equal to zero, then a warning will
+        be generated to the console, since in this case
+        the parsers may not work correctly.
+
+    Parameters:
+    -----------
+    aliases: :class:`list[str]`
+        Aliases of unit of time.
+    duration: :class:`int` = 60 * 60 * 24 * 7
+        Unit of time duration.
+    """
+
+    duration: int = 60 * 60 * 24 * 7
+
+
+@dataclass(eq=False)
+class Year(Unit):
+    """Used to create custom units of time.
+
+    !!! info
+        This subclass inherits the `__hash__` method.
+
+    !!! warning
+        If the `duration` attribute of this class is
+        less than or equal to zero, then a warning will
+        be generated to the console, since in this case
+        the parsers may not work correctly.
+
+    Parameters:
+    -----------
+    aliases: :class:`list[str]`
+        Aliases of unit of time.
+    duration: :class:`int` = 60 * 60 * 24 * 365
+        Unit of time duration.
+    """
+
+    duration: int = 60 * 60 * 24 * 365
diff --git a/aiotense/service_layer/__init__.py b/tense/service_layer/__init__.py
similarity index 100%
rename from aiotense/service_layer/__init__.py
rename to tense/service_layer/__init__.py
diff --git a/aiotense/service_layer/dot_tense/__init__.py b/tense/service_layer/dot_tense/__init__.py
similarity index 100%
rename from aiotense/service_layer/dot_tense/__init__.py
rename to tense/service_layer/dot_tense/__init__.py
diff --git a/aiotense/service_layer/dot_tense/converters.py b/tense/service_layer/dot_tense/converters.py
similarity index 95%
rename from aiotense/service_layer/dot_tense/converters.py
rename to tense/service_layer/dot_tense/converters.py
index 2c484a2..2477e05 100644
--- a/aiotense/service_layer/dot_tense/converters.py
+++ b/tense/service_layer/dot_tense/converters.py
@@ -28,7 +28,7 @@
 import abc
 from typing import Any, Hashable
 
-from aiotense.service_layer.safe_eval import SafelyExpEvalute
+from tense.service_layer.safe_eval import SafelyExpEvalute
 
 VCONVERTER_CONSTS: dict[str, int] = {
     "second": 1,
@@ -43,6 +43,8 @@
 class AbstractParticleConverter(abc.ABC):
     """Base converter for particles (tokens)."""
 
+    __slots__ = ()
+
     @abc.abstractmethod
     def convert(self, value: Any) -> Any:
         ...
@@ -60,6 +62,8 @@ class AbstractParticleValueConverter(abc.ABC):
                                 ^^^^^
     """
 
+    __slots__ = ()
+
     @abc.abstractmethod
     def matches(self, value: str) -> bool:
         ...
@@ -70,6 +74,8 @@ def convert(self, value: str) -> Any:
 
 
 class GetattributeParticleConverter(AbstractParticleConverter):
+    __slots__ = ()
+
     @staticmethod
     def _parse_exp(value: str) -> tuple[str, str]:
         key, value = [_.strip() for _ in value.split("=")]
@@ -85,6 +91,8 @@ def convert(self, value: str) -> dict[Hashable, Any]:
 
 
 class DigitValueConverter(AbstractParticleValueConverter):
+    __slots__ = ()
+
     def matches(self, value: str) -> bool:
         return value.isdigit()
 
@@ -93,6 +101,8 @@ def convert(self, value: str) -> int:
 
 
 class BooleanValueConverter(AbstractParticleValueConverter):
+    __slots__ = ()
+
     def matches(self, value: str) -> bool:
         return value.lower() in {"true", "false"}
 
@@ -101,6 +111,8 @@ def convert(self, value: str) -> bool:
 
 
 class ListValueConverter(AbstractParticleValueConverter):
+    __slots__ = ()
+
     def matches(self, value: str) -> bool:
         return len(value.split(",")) > 1
 
@@ -110,6 +122,8 @@ def convert(self, value: str) -> list[str] | str:
 
 
 class ExpressionValueConverter(AbstractParticleValueConverter):
+    __slots__ = ()
+
     def matches(self, value: str) -> bool:
         return value.startswith("exp(") and value.endswith(")")
 
diff --git a/aiotense/service_layer/dot_tense/domain.py b/tense/service_layer/dot_tense/domain.py
similarity index 91%
rename from aiotense/service_layer/dot_tense/domain.py
rename to tense/service_layer/dot_tense/domain.py
index 5b40056..addccf2 100644
--- a/aiotense/service_layer/dot_tense/domain.py
+++ b/tense/service_layer/dot_tense/domain.py
@@ -15,7 +15,7 @@
 __all__ = ["HashableParticle", "HeaderParticle", "GetattributeParticle"]
 
 import abc
-from typing import cast
+from typing import Union, cast
 
 
 class HashableParticle(abc.ABC):
@@ -23,6 +23,8 @@ class HashableParticle(abc.ABC):
     Used at the step of lexical analysis for convenience when parsing.
     """
 
+    __slots__ = ("target",)
+
     def __init__(self, target: str) -> None:
         self.target = target
 
@@ -35,7 +37,7 @@ def matches(self) -> bool:
         ...
 
     @classmethod
-    def matches_converter(cls, converter: type | object) -> bool:
+    def matches_converter(cls, converter: Union[type, object]) -> bool:
         if not hasattr(converter, "__name__"):
             converter = converter.__class__
 
@@ -50,6 +52,8 @@ class HeaderParticle(HashableParticle):
      * [...]
     """
 
+    __slots__ = ("target",)
+
     def matches(self) -> bool:
         # <inherited docstring from :class:`HashableParticle` #
         return self.target.startswith("[") and self.target.endswith("]")
@@ -66,6 +70,8 @@ class GetattributeParticle(HashableParticle):
      * variable = ...
     """
 
+    __slots__ = ("target",)
+
     def matches(self) -> bool:
         # <inherited docstring from :class:`HashableParticle` #
         return len(self.target.split("=")) > 1
diff --git a/aiotense/service_layer/dot_tense/exceptions.py b/tense/service_layer/dot_tense/exceptions.py
similarity index 100%
rename from aiotense/service_layer/dot_tense/exceptions.py
rename to tense/service_layer/dot_tense/exceptions.py
diff --git a/aiotense/service_layer/dot_tense/step_chain.py b/tense/service_layer/dot_tense/step_chain.py
similarity index 98%
rename from aiotense/service_layer/dot_tense/step_chain.py
rename to tense/service_layer/dot_tense/step_chain.py
index 63c15b8..c8734b3 100644
--- a/aiotense/service_layer/dot_tense/step_chain.py
+++ b/tense/service_layer/dot_tense/step_chain.py
@@ -33,8 +33,8 @@
     final,
 )
 
-from aiotense.domain import model, units
-from aiotense.service_layer.dot_tense import converters, domain, exceptions
+from tense.domain import model, units
+from tense.service_layer.dot_tense import converters, domain, exceptions
 
 T = TypeVar("T")
 T_co = TypeVar("T_co", covariant=True)
diff --git a/aiotense/service_layer/safe_eval.py b/tense/service_layer/safe_eval.py
similarity index 100%
rename from aiotense/service_layer/safe_eval.py
rename to tense/service_layer/safe_eval.py
diff --git a/aiotense/service_layer/unit_of_work.py b/tense/service_layer/unit_of_work.py
similarity index 84%
rename from aiotense/service_layer/unit_of_work.py
rename to tense/service_layer/unit_of_work.py
index 7bb95fc..9ead5d4 100644
--- a/aiotense/service_layer/unit_of_work.py
+++ b/tense/service_layer/unit_of_work.py
@@ -14,31 +14,22 @@
 """Unit of work for atomic alias operations."""
 from __future__ import annotations
 
-__all__ = ["AbstractTenseUnitOfWork", "TenseUnitOfWork"]
+__all__ = [
+    "AbstractTenseUnitOfWork",
+    "TenseUnitOfWork",
+]
 
 import abc
 from typing import TYPE_CHECKING, Any, Iterable, Type
 
-from aiotense.adapters import repository
-from aiotense.domain import units
+from tense.adapters import repository
+from tense.domain import units
 
 if TYPE_CHECKING:
-    from aiotense.application.ports import repository as abc_repository
+    from tense.application.ports import repository as abc_repository
 
 
-class AbstractUnitOfWork:
-    """Base class for atomic operations. Useful for annotations.
-
-    Implements in:
-        ~ :class:`AbstractTenseUnitOfWork`
-        ~ :class:`TenseUnitOfWork`
-    """
-
-    products: Any
-    pass
-
-
-class AbstractTenseUnitOfWork(AbstractUnitOfWork, abc.ABC):
+class AbstractTenseUnitOfWork(abc.ABC):
     """An abstract class whose subclasses are responsible for
     atomic operations on Tense units (remove unit aliases/replace/...).
 
@@ -46,7 +37,9 @@ class AbstractTenseUnitOfWork(AbstractUnitOfWork, abc.ABC):
         ~ :class:`TenseUnitOfWork`
     """
 
-    tenses: abc_repository.AbstractTenseRepository
+    __slots__ = ("products",)
+
+    products: abc_repository.AbstractTenseRepository
 
     def __enter__(self) -> AbstractTenseUnitOfWork:
         return self
@@ -111,7 +104,9 @@ def delete_aliases(self, unit: Type[units.Unit], aliases: Iterable[str]) -> None
 
     @abc.abstractmethod
     def replace_aliases(
-        self, unit: Type[units.Unit], replacements: dict[str, str]
+        self,
+        unit: Type[units.Unit],
+        replacements: dict[str, str],
     ) -> None:
         """Replace aliases for concrete unit of time.
 
@@ -140,27 +135,31 @@ def replace_aliases(
 class TenseUnitOfWork(AbstractTenseUnitOfWork):
     """Adapter of :class:`AbstractTenseUnitOfWork`."""
 
+    __slots__ = ("products",)
+
     def __enter__(self) -> AbstractTenseUnitOfWork:
-        self.tenses = repository.TenseRepository()
+        self.products = repository.TenseRepository()
         return super().__enter__()
 
     def update_config(self, config: dict[str, Any]) -> None:
         # <<inherited docstring from :class:`AbstractTenseUnitOfWork`>> #
-        self.tenses._config.update(config)
+        self.products.config.update(config)
 
     def delete_aliases(self, unit: Type[units.Unit], aliases: Iterable[str]) -> None:
         # <<inherited docstring from :class:`AbstractTenseUnitOfWork`>> #
         unit = self.with_unit_resolve(unit.__name__)  # type: ignore[assignment]
-        unit_aliases = self.tenses._config[unit]["aliases"]
+        unit_aliases = self.products.config[unit]["aliases"]
         for alias in aliases:
             unit_aliases.remove(alias)
 
     def replace_aliases(
-        self, unit: Type[units.Unit], replacements: dict[str, str]
+        self,
+        unit: Type[units.Unit],
+        replacements: dict[str, str],
     ) -> None:
         # <<inherited docstring from :class:`AbstractTenseUnitOfWork`>> #
         unit = self.with_unit_resolve(unit.__name__)  # type: ignore[assignment]
-        unit_aliases = self.tenses._config[unit]["aliases"]
+        unit_aliases = self.products.config[unit]["aliases"]
         for old, new in replacements.items():
             for idx, exiting_alias in enumerate(unit_aliases):
                 if old == exiting_alias:


Test patch:
diff --git a/tests/__init__.py b/tests/__init__.py
index e69de29..fd347cd 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
diff --git a/tests/bdd/__init__.py b/tests/bdd/__init__.py
index e69de29..fd347cd 100644
--- a/tests/bdd/__init__.py
+++ b/tests/bdd/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
diff --git a/tests/bdd/environment.py b/tests/bdd/environment.py
index 6166eb1..8d9b7ff 100644
--- a/tests/bdd/environment.py
+++ b/tests/bdd/environment.py
@@ -1,10 +1,23 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
 from typing import Any, Callable, Iterator, cast
 
 import parse
 from behave import register_type
 from behave.fixture import fixture, use_fixture_by_tag
 
-from aiotense.service_layer import safe_eval
+from tense.service_layer import safe_eval
 
 config_emulation = """
 [model.Tense]
diff --git a/tests/bdd/fixture.py b/tests/bdd/fixture.py
index 226b781..a3b4d59 100644
--- a/tests/bdd/fixture.py
+++ b/tests/bdd/fixture.py
@@ -1,3 +1,16 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
 from typing import Any, Callable, Hashable, MutableMapping
 
 from behave import use_fixture
diff --git a/tests/bdd/steps/__init__.py b/tests/bdd/steps/__init__.py
index e69de29..fd347cd 100644
--- a/tests/bdd/steps/__init__.py
+++ b/tests/bdd/steps/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
diff --git a/tests/bdd/steps/dot_tense_chain_step.py b/tests/bdd/steps/dot_tense_chain_step.py
index e53da8d..cb77d8d 100644
--- a/tests/bdd/steps/dot_tense_chain_step.py
+++ b/tests/bdd/steps/dot_tense_chain_step.py
@@ -1,10 +1,23 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
 from functools import partial
 from typing import Any
 
 from behave import given, then
 from hamcrest import assert_that, calling, instance_of, is_not, raises
 
-from aiotense.service_layer.dot_tense import exceptions, step_chain
+from tense.service_layer.dot_tense import exceptions, step_chain
 from tests.pyhamcrest import has_attributes
 
 
diff --git a/tests/bdd/steps/dot_tense_converters_step.py b/tests/bdd/steps/dot_tense_converters_step.py
index 6d675da..754b507 100644
--- a/tests/bdd/steps/dot_tense_converters_step.py
+++ b/tests/bdd/steps/dot_tense_converters_step.py
@@ -1,9 +1,22 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
 from typing import Any
 
 from behave import given, then
 from hamcrest import assert_that, equal_to
 
-from aiotense.service_layer.dot_tense import converters
+from tense.service_layer.dot_tense import converters
 
 
 @given(
diff --git a/tests/bdd/steps/dot_tense_domain_step.py b/tests/bdd/steps/dot_tense_domain_step.py
index 2cef7b1..57152ba 100644
--- a/tests/bdd/steps/dot_tense_domain_step.py
+++ b/tests/bdd/steps/dot_tense_domain_step.py
@@ -1,9 +1,22 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
 from typing import Any
 
 from behave import given, then
 from hamcrest import assert_that, is_
 
-from aiotense.service_layer.dot_tense import domain
+from tense.service_layer.dot_tense import domain
 
 
 @given("we have target {target} for HeaderParticle")
diff --git a/tests/conftest.py b/tests/conftest.py
index e69de29..fd347cd 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -0,0 +1,13 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
diff --git a/tests/e2e/.aiotense b/tests/e2e/.tense
similarity index 100%
rename from tests/e2e/.aiotense
rename to tests/e2e/.tense
diff --git a/tests/e2e/__init__.py b/tests/e2e/__init__.py
index e69de29..fd347cd 100644
--- a/tests/e2e/__init__.py
+++ b/tests/e2e/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
diff --git a/tests/e2e/test_complete_ui.py b/tests/e2e/test_complete_ui.py
new file mode 100644
index 0000000..333c05a
--- /dev/null
+++ b/tests/e2e/test_complete_ui.py
@@ -0,0 +1,59 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
+from typing import Any, Iterator, Union
+
+import pytest
+from hamcrest import assert_that, greater_than
+
+from tense import TenseParser, from_tense_file, resolvers
+from tense.adapters import repository
+from tense.application.ports import converters as abc_converters
+
+from ..pyhamcrest import has_attributes
+
+_TENSE_FILEDIR = "tests/e2e/.tense"
+
+
+class DoubleConverter(abc_converters.AbstractConverter[Union[int, float]]):
+    def convert(self, value: Any) -> Union[int, float]:
+        if not isinstance(value, (int, float)):
+            return NotImplemented
+        return value * 2
+
+
+@pytest.fixture(name="converter_mock")
+def converter_mock_fixture() -> Iterator[DoubleConverter]:
+    yield DoubleConverter()
+
+
+def test_basic_tense_ui() -> None:
+    assert_that(TenseParser, has_attributes("DIGIT"))
+    assert_that(TenseParser, has_attributes("TIMEDELTA"))
+
+    parser = TenseParser(
+        TenseParser.DIGIT,
+        tenses=repository.TenseRepository(from_tense_file(_TENSE_FILEDIR)),
+    )
+
+    assert_that(parser.parse("1 decade"), greater_than(0))
+
+
+def test_advanced_tense_ui(converter_mock: DoubleConverter) -> None:
+    parser = TenseParser(
+        TenseParser.DIGIT,
+        tenses=repository.TenseRepository(from_tense_file(_TENSE_FILEDIR)),
+        converter=converter_mock,
+        time_resolver=resolvers.smart_resolver,
+    )
+    assert_that(parser.parse("1decade + 1decade"), greater_than(0))
diff --git a/tests/e2e/test_ui.py b/tests/e2e/test_ui.py
deleted file mode 100644
index 7115a2f..0000000
--- a/tests/e2e/test_ui.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import asyncio
-from typing import Any
-
-from hamcrest import assert_that, instance_of, greater_than
-
-from aiotense import TenseParser, from_tense_file, resolvers
-from aiotense.application.ports import converters as abc_converters
-from ..pyhamcrest import has_attributes
-
-_TENSE_FILEDIR = "tests/e2e/.aiotense"
-
-
-def test_from_file() -> None:
-    assert_that(from_tense_file(_TENSE_FILEDIR), instance_of(dict))
-
-
-def test_basic_ui() -> None:
-    assert_that(TenseParser, has_attributes("DIGIT"))
-    assert_that(TenseParser, has_attributes("TIMEDELTA"))
-
-    parser = TenseParser(TenseParser.DIGIT, config=from_tense_file(_TENSE_FILEDIR))
-    result = asyncio.run(parser.parse("1 decade"))
-
-    assert_that(result, greater_than(0))
-
-
-def test_advanced_ui() -> None:
-    class DoubleConverter(abc_converters.AbstractConverter[int | float]):
-        async def convert(self, value: Any) -> int | float:
-            if not isinstance(value, (int, float)):
-                return NotImplemented
-            return value * 2
-
-    parser = TenseParser(
-        TenseParser.DIGIT,
-        config=from_tense_file(_TENSE_FILEDIR),
-        converter=DoubleConverter(),
-        time_resolver=resolvers.smart_resolver,
-    )
-
-    result = asyncio.run(parser.parse("1decade and 1decade"))
-    assert_that(result, greater_than(0))
diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py
index e69de29..fd347cd 100644
--- a/tests/integration/__init__.py
+++ b/tests/integration/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
diff --git a/tests/integration/test_model.py b/tests/integration/test_model.py
index 8bfb67b..20e266e 100644
--- a/tests/integration/test_model.py
+++ b/tests/integration/test_model.py
@@ -1,26 +1,56 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
 from typing import Iterator
 
 import pytest
-from hamcrest import assert_that, has_length, instance_of, greater_than
+from hamcrest import assert_that, greater_than, has_length, instance_of, is_
 
-from aiotense.domain import model, units
-from aiotense.adapters import repository
+from tense.adapters import repository
+from tense.domain import model
 
-tenses = repository.TenseRepository()
+from ..pyhamcrest import is_dataclass, subclass_of
 
 
-@pytest.fixture(name="tense")
-def tense_fixture() -> Iterator[model.Tense]:
-    yield model.Tense.from_dict(tenses.config)
+@pytest.fixture(name="tense_repository")
+def tense_repository_fixture() -> Iterator[repository.TenseRepository]:
+    yield repository.TenseRepository()
 
 
-def test_tense_cache(tense: model.Tense) -> None:
-    assert_that(tense._cached_units, has_length(0))
-    for _ in tense:
-        continue
+class TestTense:
+    def test_tense(self) -> None:
+        from collections.abc import Iterable
 
-    assert_that(len(tense._cached_units), greater_than(0))
+        assert_that(model.Tense, is_dataclass())
+        assert_that(model.Tense, is_(subclass_of(Iterable)))
 
+    def test_cache(self, tense_repository: repository.TenseRepository) -> None:
+        tense = model.Tense.from_repository(tense_repository)
+        assert_that(tense._cached_units, has_length(0))
 
-def test_tense_from_dict() -> None:
-    assert_that(model.Tense.from_dict(tenses.config), instance_of(model.Tense))
+        for _ in tense:
+            continue
+
+        assert_that(len(tense._cached_units), greater_than(0))
+
+    def test_from_dict(self, tense_repository: repository.TenseRepository) -> None:
+        assert_that(
+            model.Tense.from_dict(tense_repository.config), instance_of(model.Tense)
+        )
+
+    def test_from_repository(
+        self, tense_repository: repository.TenseRepository
+    ) -> None:
+        assert_that(
+            model.Tense.from_repository(tense_repository), instance_of(model.Tense)
+        )
diff --git a/tests/pyhamcrest.py b/tests/pyhamcrest.py
index 3fd244b..f14df44 100644
--- a/tests/pyhamcrest.py
+++ b/tests/pyhamcrest.py
@@ -1,7 +1,21 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
 from __future__ import annotations
 
-__all__ = ["subclass_of", "has_attributes", "in_"]
+__all__ = ["subclass_of", "has_attributes", "in_", "is_dataclass"]
 
+import dataclasses
 from typing import TYPE_CHECKING, Any, Optional, Sequence
 
 from hamcrest.core.base_matcher import BaseMatcher
@@ -11,8 +25,8 @@
 
 
 class IsSubclassOf(BaseMatcher):
-    def __init__(self, subclass_of: type, /) -> None:
-        self._subcls_of = subclass_of
+    def __init__(self, subcls_of: type, /) -> None:
+        self._subcls_of = subcls_of
         self.failed: Optional[str] = None
 
     def _matches(self, cls: Any) -> bool:
@@ -68,6 +82,24 @@ def describe_to(self, description: Description) -> None:
         )
 
 
+class IsDataclass(BaseMatcher):
+    def __init__(self) -> None:
+        self.failed: Any = None
+
+    def _matches(self, obj: Any) -> bool:
+        result = dataclasses.is_dataclass(obj)
+        if not result:
+            self.failed = obj
+        return result
+
+    def describe_to(self, description: Description) -> None:
+        (
+            description.append_text("failing on ").append_text(
+                f"<{self.failed}> attribute"
+            )
+        )
+
+
 def subclass_of(subcls_of: type, /) -> IsSubclassOf:
     return IsSubclassOf(subcls_of)
 
@@ -78,3 +110,7 @@ def has_attributes(*attributes: str) -> HasAttributes:
 
 def in_(seq: Sequence[Any], /) -> In:
     return In(seq)
+
+
+def is_dataclass() -> IsDataclass:
+    return IsDataclass()
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
index e69de29..fd347cd 100644
--- a/tests/unit/__init__.py
+++ b/tests/unit/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
diff --git a/tests/unit/test_domain.py b/tests/unit/test_domain.py
deleted file mode 100644
index 687226a..0000000
--- a/tests/unit/test_domain.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from hamcrest import assert_that
-
-from aiotense.domain import units
-from ..pyhamcrest import subclass_of
-
-
-def test_hierarchy_units() -> None:
-    assert_that(units.VirtualUnit, subclass_of(units.Unit))
-    assert_that(units.Second, subclass_of(units.Unit))
-    assert_that(units.Minute, subclass_of(units.Unit))
-    assert_that(units.Hour, subclass_of(units.Unit))
-    assert_that(units.Day, subclass_of(units.Unit))
-    assert_that(units.Week, subclass_of(units.Unit))
-    assert_that(units.Year, subclass_of(units.Unit))
diff --git a/tests/unit/test_resolvers.py b/tests/unit/test_resolvers.py
new file mode 100644
index 0000000..be226d0
--- /dev/null
+++ b/tests/unit/test_resolvers.py
@@ -0,0 +1,61 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
+from typing import Iterator
+
+import pytest
+from hamcrest import assert_that, equal_to
+
+from tense import model, resolvers
+from tense.adapters import repository
+
+
+@pytest.fixture(name="tense")
+def tense_fixture() -> Iterator[model.Tense]:
+    yield model.Tense.from_repository(repository.TenseRepository())
+
+
+@pytest.mark.parametrize(
+    "string_to_parse,result",
+    (
+        ("1d1min", ["1", "d", "1", "min"]),
+        ("1d 1min", ["1", "d", "1", "min"]),
+        ("1d1min 2 seconds", ["1", "d", "1", "min", "2", "sec"]),
+    ),
+)
+def test_smart_resolver(
+    string_to_parse: str, result: list[str], tense: model.Tense
+) -> None:
+    assert_that(
+        list(resolvers.smart_resolver(string_to_parse, tense)), equal_to(result)
+    )
+
+
+@pytest.mark.parametrize(
+    "string_to_parse,result",
+    (
+        ("1d1min", ["1", "d", "1", "min"]),
+        ("1d 1min", ["1", "d", "1", "min"]),
+        ("1d1min 2 seconds", ["1", "d", "1", "min", "2", "seconds"]),
+        (
+            "1year and 10 minutes + 5 seconds",
+            ["1", "yearand", "10", "minutes+", "5", "seconds"],
+        ),
+    ),
+)
+def test_basic_resolver(
+    string_to_parse: str, result: list[str], tense: model.Tense
+) -> None:
+    assert_that(
+        list(resolvers.basic_resolver(string_to_parse, tense)), equal_to(result)
+    )
diff --git a/tests/unit/test_safe_eval.py b/tests/unit/test_safe_eval.py
index 5fdca77..bcc7f31 100644
--- a/tests/unit/test_safe_eval.py
+++ b/tests/unit/test_safe_eval.py
@@ -1,10 +1,24 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
 from typing import Final
 
-from hamcrest import assert_that, equal_to
+from hamcrest import assert_that, equal_to, is_in
 
-from aiotense.service_layer import safe_eval
+from tense.service_layer import safe_eval
 
-CONSTANT: Final[float] = 3.14
+_CONSTANT: Final[float] = 3.14
+_CONSTANT_NAME: Final[str] = "_CONSTANT"
 
 
 def test_eval_basics() -> None:
@@ -21,5 +35,10 @@ def test_eval_flexibility() -> None:
 
 
 def test_eval_safety() -> None:
-    safe_eval.SafelyExpEvalute("globals().pop('CONSTANT')")
-    assert "CONSTANT" in globals()
+    from operator import itemgetter
+
+    safe_eval.SafelyExpEvalute(f"globals().pop('{_CONSTANT_NAME}')")
+    assert_that(_CONSTANT_NAME, is_in(globals()))
+
+    safe_eval.SafelyExpEvalute("delattr(__builtins__, 'int')")
+    assert_that(itemgetter("int")(__builtins__), equal_to(int))
diff --git a/tests/unit/test_unit_of_work.py b/tests/unit/test_unit_of_work.py
index 87cf850..74eaa88 100644
--- a/tests/unit/test_unit_of_work.py
+++ b/tests/unit/test_unit_of_work.py
@@ -1,61 +1,100 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
 from contextlib import AbstractContextManager
 
-from hamcrest import assert_that, greater_than, is_, is_in, not_
+from hamcrest import assert_that, equal_to, greater_than, has_length, is_, is_in, not_
 
-from aiotense.domain import units
-from aiotense.service_layer import unit_of_work as uow
+from tense.service_layer import unit_of_work as uow
 
 from ..pyhamcrest import has_attributes, in_, subclass_of
 
+_BASE_SETTING_GROUP = "model.Tense"
+_MULTIPLIER_SETTING = "multiplier"
+_SECOND_SETTING = "units.Second"
+_ALIASES_SETTING = "aliases"
 
-def test_hierarchy() -> None:
-    assert_that(uow.AbstractTenseUnitOfWork, is_(subclass_of(uow.AbstractUnitOfWork)))
-    assert_that(uow.TenseUnitOfWork, is_(subclass_of(uow.AbstractTenseUnitOfWork)))
 
+class TestTenseUnitOfWork:
+    def test_tense_unit_of_work(self) -> None:
+        assert_that(uow.TenseUnitOfWork, is_(subclass_of(uow.AbstractTenseUnitOfWork)))
+        assert_that(
+            uow.AbstractTenseUnitOfWork, is_(subclass_of(AbstractContextManager))
+        )
 
-def test_syntax_sugar() -> None:
-    assert_that(uow.AbstractTenseUnitOfWork, is_(subclass_of(AbstractContextManager)))
+        tense_uow = uow.TenseUnitOfWork()
+        assert_that(tense_uow, not_(has_attributes("products")))
 
+        with tense_uow:
+            assert_that(tense_uow, has_attributes("products"))
 
-def test_basic() -> None:
-    with uow.TenseUnitOfWork() as tense_uow:
-        assert_that(tense_uow, has_attributes("tenses"))
-
-
-class TestAtomicOperations:
     def test_update_config(self) -> None:
-        with uow.TenseUnitOfWork() as tense_uow:
-            # Before update
-            model_tense = tense_uow.tenses.config["model.Tense"]
-            initial_mul = model_tense["multiplier"]  # By default
-
-            tense_uow.update_config({"model.Tense": {"multiplier": initial_mul + 1}})
-
-            # After update
-            model_tense = tense_uow.tenses.config["model.Tense"]
-            assert_that(model_tense["multiplier"], greater_than(initial_mul))
+        tense_uow = uow.TenseUnitOfWork()
+
+        with tense_uow:
+            before_multiplier = tense_uow.products.config[_BASE_SETTING_GROUP][
+                _MULTIPLIER_SETTING
+            ]
+            assert_that(before_multiplier, equal_to(1))
+
+            tense_uow.update_config(
+                {
+                    _BASE_SETTING_GROUP: {
+                        _MULTIPLIER_SETTING: 2,
+                    }
+                }
+            )
+
+            # multiplier changed
+            after_multiplier = tense_uow.products.config[_BASE_SETTING_GROUP][
+                _MULTIPLIER_SETTING
+            ]
+            assert_that(after_multiplier, equal_to(2))
+
+            # other values are saved
+            assert_that(tense_uow.products.config, has_length(greater_than(1)))
 
     def test_delete_aliases(self) -> None:
-        with uow.TenseUnitOfWork() as tense_uow:
-            # Before delete
-            second = tense_uow.tenses.config["units.Second"]
-            assert_that("s", is_in(second["aliases"]))
+        from tense import units
 
-            tense_uow.delete_aliases(units.Second, ("s",))
+        tense_uow = uow.TenseUnitOfWork()
 
-            # After delete
-            second = tense_uow.tenses.config["units.Second"]
-            assert_that("s", not_(in_(second["aliases"])))
+        with tense_uow:
+            before_second_aliases = tense_uow.products.get_setting(
+                _SECOND_SETTING, _ALIASES_SETTING
+            )
+            assert_that("s", is_in(before_second_aliases))
+
+            tense_uow.delete_aliases(units.Second, ["s"])
+            after_second_aliases = tense_uow.products.get_setting(
+                _SECOND_SETTING, _ALIASES_SETTING
+            )
+            assert_that("s", not_(in_(after_second_aliases)))
 
     def test_replace_aliases(self) -> None:
-        with uow.TenseUnitOfWork() as tense_uow:
-            # Before replace
-            second = tense_uow.tenses.config["units.Second"]
-            assert_that("sec", is_in(second["aliases"]))
+        from tense import units
 
-            tense_uow.replace_aliases(units.Second, {"sec": "ssec"})
+        tense_uow = uow.TenseUnitOfWork()
 
-            # After replace
-            second_aliases = tense_uow.tenses.config["units.Second"]["aliases"]
-            assert_that("sec", not_(in_(second_aliases)))
-            assert_that("ssec", is_in(second_aliases))
+        with tense_uow:
+            before_second_aliases = tense_uow.products.get_setting(
+                _SECOND_SETTING, _ALIASES_SETTING
+            )
+            assert_that("sec", is_in(before_second_aliases))
+
+            tense_uow.replace_aliases(units.Second, {"sec": "ssec"})
+            after_second_aliases = tense_uow.products.get_setting(
+                _SECOND_SETTING, _ALIASES_SETTING
+            )
+            assert_that("sec", not_(in_(after_second_aliases)))
+            assert_that("ssec", is_in(after_second_aliases))
diff --git a/tests/unit/test_units.py b/tests/unit/test_units.py
new file mode 100644
index 0000000..030d45f
--- /dev/null
+++ b/tests/unit/test_units.py
@@ -0,0 +1,98 @@
+# Copyright 2022 Animatea
+#
+# Licensed 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
+#
+#     http://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.
+"""
+!!! note
+    Default `duration` attribute values are not tested because they are not constants.
+"""
+from collections.abc import Hashable
+from typing import Iterator, Union
+
+import pytest
+from hamcrest import assert_that, has_length, is_
+
+from tense import units
+
+from ..pyhamcrest import is_dataclass, subclass_of
+
+
+@pytest.fixture(name="mock_kwargs")
+def unit_kwargs_fixture() -> Iterator[dict[str, Union[list[str], int]]]:
+    yield {
+        "duration": 0,
+        "aliases": [],
+    }
+
+
+def test_unit_base(mock_kwargs: dict[str, Union[list[str], int]]) -> None:
+    import warnings
+
+    assert_that(units.Unit, is_dataclass())
+
+    with warnings.catch_warnings(record=True) as warns:
+        assert_that(warns, has_length(0))
+
+        _ = units.Unit(**mock_kwargs)
+
+        assert_that(warns, has_length(1))
+
+    assert_that(units.Unit, is_(subclass_of(Hashable)))
+
+
+def test_virtual_unit() -> None:
+    assert_that(units.VirtualUnit, is_(subclass_of(units.Unit)))
+    assert_that(units.VirtualUnit, is_dataclass())
+    # Hashable dataclass
+    assert_that(units.VirtualUnit, is_(subclass_of(Hashable)))
+
+
+def test_second() -> None:
+    assert_that(units.Second, is_(subclass_of(units.Unit)))
+    assert_that(units.Second, is_dataclass())
+    # Hashable dataclass
+    assert_that(units.Second, is_(subclass_of(Hashable)))
+
+
+def test_minute() -> None:
+    assert_that(units.Minute, is_(subclass_of(units.Unit)))
+    assert_that(units.Minute, is_dataclass())
+    # Hashable dataclass
+    assert_that(units.Minute, is_(subclass_of(Hashable)))
+
+
+def test_hour() -> None:
+    assert_that(units.Hour, is_(subclass_of(units.Unit)))
+    assert_that(units.Hour, is_dataclass())
+    # Hashable dataclass
+    assert_that(units.Hour, is_(subclass_of(Hashable)))
+
+
+def test_day() -> None:
+    assert_that(units.Day, is_(subclass_of(units.Unit)))
+    assert_that(units.Day, is_dataclass())
+    # Hashable dataclass
+    assert_that(units.Day, is_(subclass_of(Hashable)))
+
+
+def test_week() -> None:
+    assert_that(units.Week, is_(subclass_of(units.Unit)))
+    assert_that(units.Week, is_dataclass())
+    # Hashable dataclass
+    assert_that(units.Week, is_(subclass_of(Hashable)))
+
+
+def test_year() -> None:
+    assert_that(units.Year, is_(subclass_of(units.Unit)))
+    assert_that(units.Year, is_dataclass())
+    # Hashable dataclass
+    assert_that(units.Year, is_(subclass_of(Hashable)))