{"id":28580927,"url":"https://github.com/techsenger/mvvm4fx","last_synced_at":"2025-10-27T15:08:10.818Z","repository":{"id":296624503,"uuid":"927703673","full_name":"techsenger/mvvm4fx","owner":"techsenger","description":"Techsenger MVVM4FX is a tiny framework for developing JavaFX applications using MVVM pattern.","archived":false,"fork":false,"pushed_at":"2025-10-26T08:22:53.000Z","size":117,"stargazers_count":12,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-26T10:15:53.326Z","etag":null,"topics":["javafx","javafx-11","javafx-application","javafx-components","javafx-desktop-apps","javafx-gui","javafx-library","javafx-project","mvvm","mvvm-architecture","mvvm-framework","mvvm-pattern","mvvm-sample"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/techsenger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"techsenger"}},"created_at":"2025-02-05T12:02:48.000Z","updated_at":"2025-10-26T08:22:57.000Z","dependencies_parsed_at":"2025-10-18T15:09:00.668Z","dependency_job_id":null,"html_url":"https://github.com/techsenger/mvvm4fx","commit_stats":null,"previous_names":["techsenger/mvvm4fx"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/techsenger/mvvm4fx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techsenger%2Fmvvm4fx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techsenger%2Fmvvm4fx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techsenger%2Fmvvm4fx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techsenger%2Fmvvm4fx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/techsenger","download_url":"https://codeload.github.com/techsenger/mvvm4fx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techsenger%2Fmvvm4fx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281287756,"owners_count":26475419,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-27T02:00:05.855Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["javafx","javafx-11","javafx-application","javafx-components","javafx-desktop-apps","javafx-gui","javafx-library","javafx-project","mvvm","mvvm-architecture","mvvm-framework","mvvm-pattern","mvvm-sample"],"created_at":"2025-06-11T04:14:27.270Z","updated_at":"2025-10-27T15:08:10.809Z","avatar_url":"https://github.com/techsenger.png","language":"Java","funding_links":["https://github.com/sponsors/techsenger"],"categories":[],"sub_categories":[],"readme":"# Techsenger MVVM4FX\n\nTechsenger MVVM4FX is a tiny framework for developing JavaFX applications using the MVVM pattern. It provides all\nthe necessary interfaces and base class implementations for creating components, which serve as the units of the MVVM\npattern. Examples of components include tabs, dialog windows, toolbars, image viewers, help pages, and more.\n\n## Table of Contents\n* [Features](#features)\n* [MVVM](#mvvm)\n    * [What is MVVM?](#what-is-mvvm)\n    * [MVVM Advantages](#mvvm-advantages)\n* [Component](#component)\n    * [What is a Component?](#what-is-component)\n    * [Component Structure](#component-structure)\n    * [Component Key](#component-key)\n    * [Component Lifecycle](#component-lifecycle)\n    * [Component Hierarchy](#component-hierarchy)\n* [Requirements](#requirements)\n* [Dependencies](#dependencies)\n* [Code building](#code-building)\n* [Running Demo](#demo)\n* [License](#license)\n* [Contributing](#contributing)\n* [Support Us](#support-us)\n\n## Features \u003ca name=\"features\"\u003e\u003c/a\u003e\n\nKey features include:\n\n* Support for the component lifecycle.\n* Organization of core tasks within the view.\n* Component inheritance.\n* Ability to preserve component history.\n* Designed without considering FXML support.\n* Detailed documentation and sample code.\n\n## MVVM \u003ca name=\"mvvm\"\u003e\u003c/a\u003e\n\n### What is MVVM? \u003ca name=\"what-is-mvvm\"\u003e\u003c/a\u003e\n\nMVVM (Model-View-ViewModel) is an architectural pattern that divides an application's logic into three main parts:\nModel, View, and ViewModel.\n\nModel — encapsulates the data and business logic of the application. Models represent an abstraction that stores and\nprocesses the application’s data, including all business logic rules and data validation logic. Models do not interact\nwith the UI and do not know about View or ViewModel. Instead, they provide data and perform actions related to the\nbusiness logic. Model can include:\n\n* Data (for example, entities from a database or objects obtained from external sources).\n* Business logic (such as data processing rules, calculations, data manipulation).\n* Validation logic (for example, checks that are performed before saving data).\n\nView — represents the user interface that displays the data. The View's task is to contain UI elements and bind their\nstate to the ViewModel. View is responsible for displaying data and interacting with the user, but it should not\ncontain logic for managing the state of these elements. Because it is the responsibility of the ViewModel to control\nthis state without knowing about specific controls in the View. For example, if the ViewModel indicates that a button\nshould be active or inactive, the View will update the control, but the View will not manage the logic that determines\nwhen the button should be enabled or disabled.\n\nBesides, the View may and should contain logic related to the visual behavior and layout of elements (presentation\nlogic). This includes calculating positions and sizes, managing component arrangement (e.g., docking or resizing),\nhandling animations, drag-and-drop operations, or other view-related interactions that depend on specific UI components.\n\nViewModel — manages the state of UI elements without needing to know the implementation details of the user interface.\nViewModel can also serve as a layer between the View and Model, obtaining data from the Model and preparing it for\ndisplay in the View. It can transform the data from the model into a format suitable for UI presentation.\n\n### MVVM Advantages \u003ca name=\"mvvm-advantages\"\u003e\u003c/a\u003e\n\n* Separation of concerns. MVVM helps to clearly separate the presentation logic (View), business logic and data (Model),\nand interaction logic (ViewModel). This simplifies code maintenance and makes it more readable.\n\n* Testability. The ViewModel can be tested independently of the user interface (UI) because it is not tied to specific\nvisual elements. This makes it easy to write unit tests for business logic.\n\n* Two-way data binding. In MVVM, data is automatically synchronized between the View and ViewModel, which reduces the\namount of code required for managing UI state and simplifies updates.\n\n* Simplification of complex UIs. When an application has complex UIs with dynamic data, MVVM helps make the code more\nunderstandable and structured, easing management of UI element states.\n\n* UI updates without direct manipulation. The ViewModel manages updates to the View via data binding, avoiding direct\nmanipulation of UI elements. This makes the code more flexible and scalable.\n\n## Component \u003ca name=\"component\"\u003e\u003c/a\u003e\n\n### What is a Component? \u003ca name=\"what-is-component\"\u003e\u003c/a\u003e\n\nA component is a fundamental, self-contained building block of a user interface (UI) that provides a specific\npiece of functionality and enables user interaction. A component represents a higher-level abstraction than standard\nUI controls, fundamentally distinguished by its compositional nature, which encompasses and organizes multiple\nUI controls, its managed lifecycle, and its capacity to maintain state history. Crucially, while usually components\nalso encapsulate business logic, this is not a mandatory trait for all, as structural components like layout containers\ndemonstrate.\n\n### Component Structure \u003ca name=\"component-structure\"\u003e\u003c/a\u003e\n\nA component always consists of at least two classes: a `ComponentViewModel` and a `ComponentView`. A natural question\nmight arise: why is there no `Model` in the component, given that the pattern is called MVVM? Firstly, a component\nis a building block for constructing a user interface, which might not be related to the application's business logic\nat all. Secondly, the `Model` exists independently of the UI and should have no knowledge of the component's existence.\n\nIt is important to note, that in addition to its standard functions the `ComponentView` is responsible for managing the\ncreation and removal of only two types of components: its own child components and components with an externally\nprovided API, such as dialogs or popup windows. This limitation exists because the ComponentView can only fully manage\nelements within its own scope and awareness.\n\nIn addition to the `ComponentViewModel` and `ComponentView`, a component may include two optional classes:\n`ComponentHistory` and `ComponentBridge`.\n\nThe `ComponentHistory` enables the preservation of the component's state upon its destruction. Data exchange occurs\nexclusively between the `ComponentViewModel` and the `ComponentHistory`. During component construction, data is restored\nfrom the `ComponentHistory` to the `ComponentViewModel`, while during deinitialization, data from the\n`ComponentViewModel` is saved to the `ComponentHistory`. The volume of state history that is restored and saved is\nconfigured via the HistoryPolicy enum.\n\nThe `ComponentBridge` is an interface that allows the `ComponentViewModel` to request the `ComponentView` to perform\nspecific actions. These actions are typically related to creating or removing other components — operations that cannot\nbe executed solely within the `ComponentViewModel`. It is important to emphasize that the `ComponentViewModel` must\nnever hold a direct reference to the `ComponentView`, and the use of this interface does not violate this rule.\nThe bridge can be created and set in the `AbstractParentViewModel` either by overriding the\n`AbstractParentView#createBridge()` method or by using `AbstractParentViewModel#setBridge(ComponentBridge)`\n\n### Component Key \u003ca name=\"component-key\"\u003e\u003c/a\u003e\n\nEvery component has a unique key, which is used for component identification and can also be utilized to identify\nobjects created by the component (such as events, messages, etc.). To facilitate this, the library provides the\n`ComponentKey` interface, allowing developers to implement keys in various ways—using enums, constants, and other\nmethods—based on their specific needs.\n\nThe need for a key arises from the fact that a component's own classes may reside within private packages of a\nmodule and thus be inaccessible for identification purposes. The use of keys solves this problem, as keys can and\nshould be placed in publicly accessible (exported) packages.\n\n### Component Lifecycle \u003ca name=\"component-lifecycle\"\u003e\u003c/a\u003e\n\nA component has four distinct states (see `ComponentState`):\n\n1. Unconstructed - The component has not yet been constructed (`ComponentViewModel` exists, but `ComponentView` has\nnot been created).\n\n2. Constructed - Both the `ComponentViewModel` and `ComponentView` have been created, but the component is not yet\ninitialized. It is important to note that when the component transitions to this state, the `ComponentViewModel` state\nis restored from the `ComponentHistory`.\n\n3. Initialized - Both the `ComponentViewModel` and `ComponentView` have been fully initialized and are ready for use.\nThe component enters this state upon completion of the `ComponentView#initialize(`) method, but before the call to the\n`AbstractComponentView#postInitialize()` method.\n\n4. Deinitialized - The component has been deinitialized and can't be used anymore. It enters this state upon\ncompletion of the `ComponentView#deinitialize()` method, but before the call to the\n`AbstractComponentView#postDeinitialize()` method. It is important to note that when the component transitions to this\nstate, the `ComponentViewModel` state is saved to the `ComponentHistory`.\n\nEach component features `ComponentView#initialize()` and `ComponentView#deinitialize()` methods, which initialize and\ndeinitialize the component, respectively, altering its state. The default implementation of these methods in\n`AbstractComponentView` is achieved through template methods that handle component building/unbuilding, binding/unbinding,\nadding/removing listeners, and adding/removing handlers via corresponding protected methods. It is important to note\nthat these protected methods should not be considered the exclusive location for performing such tasks (e.g.,\nadding/removing handlers) within the component, but rather as part of the initialization/deinitialization process.\nThus, adding/removing handlers may also be performed in other methods of the component.\n\n### Component Hierarchy \u003ca name=\"component-hierarchy\"\u003e\u003c/a\u003e\n\nComponents can act as both parents and children, forming a tree structure that can change dynamically.\nThe library provides a mechanism for dynamically creating and removing components and includes optional logic\nfor managing component relationships, leaving their use to the developer's discretion.\n\nThe component tree is built according to the Unidirectional Hierarchy Rule (UHR). This rule establishes a strict\nhierarchical order by explicitly prohibiting circular parent-child relationships, meaning a component cannot\nsimultaneously be a direct parent and a direct child of another component. The UHR is designed to maintain a clear,\nacyclic structure, which prevents logical conflicts and ensures predictable behavior. Importantly, this rule does not\nrestrict child components from directly accessing or communicating with their parents; it solely forbids cyclical\ndependencies that would compromise the architectural integrity of the hierarchy.\n\nIt is crucial to highlight the interaction between components. Consider a parent and a child component as an example.\nThe parent component's `ComponentViewModel` holds a reference to the child component's `ComponentViewModel` via its\n`children` field, while the child component's `ComponentViewModel` holds a reference to the parent component's\n`ComponentViewModel` via its `parent` field. Similarly, the parent component's `ComponentView` holds a reference to the\nchild component's `ComponentView` through its `children` field, and the child component's `ComponentView` holds a\nreference to the parent component's `ComponentView` via its `parent` field.\n\nThis dualistic linkage establishes a coherent and symmetric relationship between parent and child components at both\nthe View and ViewModel layers. The parent and child components are fully aware of each other's existence and state,\nenabling direct coordination and communication within the hierarchy while maintaining clear separation of concerns\nbetween the presentation (View) and logic (ViewModel) layers. This design ensures consistency and synchronization\nacross the component tree without violating the Unidirectional Hierarchy Rule (UHR), as the relationships are strictly\nhierarchical and non-cyclic.\n\n## Requirements \u003ca name=\"requirements\"\u003e\u003c/a\u003e\n\nJava 11+ and JavaFX 19.\n\n## Dependencies \u003ca name=\"dependencies\"\u003e\u003c/a\u003e\n\nThe project will be added to the Maven Central repository in a few days.\n\n## Code Building \u003ca name=\"code-building\"\u003e\u003c/a\u003e\n\nTo build the library use standard Git and Maven commands:\n\n    git clone https://github.com/techsenger/mvvm4fx\n    cd mvvm4fx\n    mvn clean install\n\n## Running Demo \u003ca name=\"demo\"\u003e\u003c/a\u003e\n\nTo run the demo execute the following commands in the root of the project:\n\n    cd mvvm4fx-demo\n    mvn javafx:run\n\nPlease note, that debugger settings are in `mvvm4fx-demo/pom.xml` file.\n\n## License \u003ca name=\"license\"\u003e\u003c/a\u003e\n\nTechsenger MVVM4FX is licensed under the Apache License, Version 2.0.\n\n## Contributing \u003ca name=\"contributing\"\u003e\u003c/a\u003e\n\nWe welcome all contributions. You can help by reporting bugs, suggesting improvements, or submitting pull requests\nwith fixes and new features. If you have any questions, feel free to reach out — we’ll be happy to assist you.\n\n## Support Us \u003ca name=\"support-us\"\u003e\u003c/a\u003e\n\nYou can support our open-source work through [GitHub Sponsors](https://github.com/sponsors/techsenger).\nYour contribution helps us maintain projects, develop new features, and provide ongoing improvements.\nMultiple sponsorship tiers are available, each offering different levels of recognition and benefits.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechsenger%2Fmvvm4fx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechsenger%2Fmvvm4fx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechsenger%2Fmvvm4fx/lists"}