{"id":20494370,"url":"https://github.com/voltra/mvea","last_synced_at":"2026-02-17T00:31:17.129Z","repository":{"id":133009139,"uuid":"163313369","full_name":"Voltra/mvea","owner":"Voltra","description":"MVEA Architectural Pattern : Model View Endpoint Action","archived":false,"fork":false,"pushed_at":"2020-07-25T11:27:15.000Z","size":466,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-12T14:36:22.911Z","etag":null,"topics":["architectural-patterns","design-patterns","hacktoberfest","mvc","software-architecture"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Voltra.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-27T16:06:00.000Z","updated_at":"2022-06-07T11:23:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"a52c4536-bebf-49c8-8370-139b54629cac","html_url":"https://github.com/Voltra/mvea","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Voltra/mvea","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Voltra%2Fmvea","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Voltra%2Fmvea/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Voltra%2Fmvea/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Voltra%2Fmvea/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Voltra","download_url":"https://codeload.github.com/Voltra/mvea/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Voltra%2Fmvea/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29526658,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T21:45:09.491Z","status":"ssl_error","status_checked_at":"2026-02-16T21:44:58.452Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["architectural-patterns","design-patterns","hacktoberfest","mvc","software-architecture"],"created_at":"2024-11-15T17:39:19.234Z","updated_at":"2026-02-17T00:31:17.085Z","avatar_url":"https://github.com/Voltra.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# MVEA Architectural Pattern\n\n## Name\n\n***MVEA***, which stands for **M**odel **V**iew **E**ndpoint **A**ction. In the world of web development, it might also be referred to as ***MVRA***, which stands for **M**odel **V**iew **R**oute **A**ction.\n\n\n\n## Problem\n\nNeed of a way to separate the use of data from its source, its manipulation as well as how it is provided to the end user.\n\n\n\n## Solution\n\n***MVEA*** is a derived form of [***MVC***](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). It divides the application in four parts :\n\n* The **model** which is directly responsible for managing the application's data\n* The **view** which is a representation of the **model** in a particular format\n* The **endpoint**(s) which is(are) the main interaction(s) with the end user, it is the set of available behavior\n* The **action** which manipulates and updates the **model**, it is the main logic behind all available behavior\n\n\n\nBelow is a graph describing the general functioning of a generic application that uses the ***MVEA*** pattern :\n\n\u003ccenter\u003e\u003cimg width=\"400px\" src=\"res/interactions.png\" alt=\"functioning\"/\u003e\u003c/center\u003e\n\n### General\n\nWhile **endpoints** represent the points of interaction in the user interface, it is the **actions** that do the heavy lifting, **endpoints** being there solely as a \"skeleton\" of code which depends on the \"muscles\" that are the **actions**.\n\n\n\n### Model\n\nAs in ***MVC​*** the **model** is responsible for providing, holding and accessing data. It is usually the component that deals with interfacing with the application's data source/database.\n\n\n\n### View\n\nThe **view** is what is delivered to the end user. Usually done via a [GUI](https://en.wikipedia.org/wiki/Graphical_user_interface), it exposes the data in a given format as well as providing the end user with ways to interact with/manipulate it.\n\n\n\n### Endpoint\n\nThe **endpoint** describes how a given interaction between the user and the **model** should be executed. It usually relies on a declarative style since all the heavy lifting is done by the **actions** it uses.\n\nUnless there is a binding between **model** and **view** (e.g. with modern web frameworks), the **endpoint** is also responsible for updating the view if there is a need to do so.\n\n\n\n### Action\n\n**Actions** tend to be reused in other contexts (e.g. in middlewares), one more reason for separating them from **endpoints** : it makes more sense since we only want to borrow some behavior, not the entire **endpoint**'s logic.\n\nThey provide a given set of tools for a *specific* need (e.g. authentication, security, session management, etc...).\n\n\n\n## Consequences\n\n***MVEA*** allows more separation of concerns than regular ***MVC*** due to the decoupling of what would have been the **controller**. Often in ***MVC*** you are forced to write \"helper classes\" to abstract away most of the heavy lifting. In this scenario, the **controller** usually becomes the **endpoint** and the **helper classes** the **actions**.\n\n\n\n## Exemples\n\nLet's use a fictitious programming language to demonstrate the use of ***MVEA***. As **models** and **views** are already common (cf. [***MVC***](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller), [***MVVM***](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel), etc...) and easy to grasp, I will only focus on the endpoints here. **Actions** are also relatively easy to grasp (the name is enough to imagine the implementation) and I will therefore not cover them.\n\n\n\nActual full examples will be provided as subdirectories that you are free to explore.\n\n\n\n### Web development\n\n```js\nimport Namespace\nimport Status\nimport DI\n@Namespace import Action\n\nconst app = App(env[\"DB\", \"SSL_CERT\", \"SSL_KEY\"]);\napp.setRenderer( Views(env[\"VIEWS_ROOT\", \"\"]) );\nDI::bootstrap(app, env[\"DI_CONFIG_FILE\"]); //use Dependency Injection via app, for app\napp.bootstrapDB().bootstrapSSL().useRouteNamesFirst();\n```\n\n\n\n```js\n@DI($flash = Actions::Flash)\napp.get(\"/\", (req, res) =\u003e {//endpoint\n    return this.render(\"home\", [\"flash\": $flash.consumeAll()]);\n}).name(\"home\");\n```\n\n\n\n```js\n@DI($auth = Actions::Auth, $flash = Actions::Flash)\napp.get(\"/login\", (_, res) =\u003e {//endpoint\n    if($auth.isAlreadyLogged())\n        return res.to(\"home\").withFlash(info: \"already logged in\");\n    \n    return this.render(\"login\", [\"flash\": $flash.consumeAll()]);\n}).name(\"login\");\n```\n\n\n\n```js\n@DI($auth = Actions::Auth, $valid = Actions::Valid, $hash = Actions::Hash)\napp.post(\"/login\", (req, res) =\u003e {//endpoint\n    const err = !$valid.exists(req[\"username\", \"password\", \"pconfirm\"]);\n    if(err)\n        return Status::BadRequest;\n    \n    if($auth.isAlreadyLogged())\n    \treturn Status::Forbidden;\n    \n    const post = req[\"username\", \"password\", \"pconfirm\"];\n    if(post[\"password\"] != post[\"pconfirm\"])\n        return res.to(\"login\").withFlash(error: \"passwords don't match\");\n    \n    const [usn, pwd] = post[\"username\", \"password\"];    \n    const user = $auth.login(usn, $hash.hash(pwd));\n    if(user){\n        Auth.setUser(user);\n        return res.to(\"home\").withFlash(success: \"logged in successfully\");\n    }else\n        return res.to(\"login\").withFlash(error: \"invalid credentials\");\n}).name(\"login.post\");\n```\n\n\n\n```js\n\nconst http = \"localhost:80\";\nconst https = \"localhost:443\";\napp.transfer(from: http, to: https).listen([http, https]);\n```\n\n\n\nAs you can see above, most of the logic is written down without any implementation. Most of the code is either branching or error handling : it is not entirely focused on the actual logic behind everything but more on how to orchestrate it.\n\n\n\n### Desktop application\n\n```js\nimport Namespace\nimport DI\nimport Endpoint\nimport LoginView\nimport HomeView\nimport GUI::AbstractInput;\n@Namespace import Models\n@Namespace import Actions\n@Namespace import Errors\n\n@DI($auth = Actions::Auth, $hash = Actions::Hash, $views = Actions::Views)\nexport class LoginEndpoint : Endpoint{\n    public:\n    \t@Static\n        @Endpoint::anchor(class = LoginView)\n        handler attach = (LoginView view) =\u003e {\n            const inputs = [\n            \tview.get(\"Input/username\"),\n            \tview.get(\"PasswordInput/password\"),\n            \tview.get(\"PasswordInput/pconfirm\")\n            ].map(e =\u003e cast\u003cAbstractInput\u003e(e));\n            /*Think of this as a magical autowiring of inputs' values*/\n            const bound = $views.bind(this::attemptLogin, inputs);\n            \n            view.get(\"Button/login\").on(\"click\", bound);\n            //attach it to the button\n            //but you can also attach it to each input field\n            inputs.forEach(input =\u003e input.on(\"enterPressed\", bound));\n        };\n    \n        void attemptLogin(string username, string password, string pconfirm){\n            if($auth.isAlreadyLogged())\n                throw Errors::InvalidState(\"Attempt to log in while logged in\");\n            \n            if(password != pconfirm){\n                $views.flash.push(error: \"Passwords don't match\");\n            \treturn;\n            }\n            \n            const pwd = $hash.hash(password);\n            const user = $auth.login(username, pwd);\n            if(user){\n                $auth.setUser(user);\n                $views.flash.pushAfterSwap(success: \"Successfully logged in\");\n                $views.swapTo(class: HomeView);\n                return;\n            }\n            \n            $views.flash.push(error: \"Invalid credentials\");\n        }\n};\n```\n\nHere again, no implementation and just layout. Once all the implementation details are abstracted away in **actions** all we have left is error handling and \"side effects\" on the **view(s)**.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvoltra%2Fmvea","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvoltra%2Fmvea","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvoltra%2Fmvea/lists"}