{"id":19762627,"url":"https://github.com/replicant4j/replicant","last_synced_at":"2026-06-02T06:01:05.785Z","repository":{"id":57740007,"uuid":"2624179","full_name":"replicant4j/replicant","owner":"replicant4j","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-29T05:21:38.000Z","size":8740,"stargazers_count":7,"open_issues_count":7,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-05-29T07:20:27.508Z","etag":null,"topics":["entity","gwt","java","network"],"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/replicant4j.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2011-10-22T01:08:17.000Z","updated_at":"2026-05-29T05:21:40.000Z","dependencies_parsed_at":"2024-05-31T06:23:12.392Z","dependency_job_id":"d63c6f79-4e32-4b41-88e7-2087c3fd85b6","html_url":"https://github.com/replicant4j/replicant","commit_stats":null,"previous_names":["realityforge/replicant"],"tags_count":398,"template":false,"template_full_name":null,"purl":"pkg:github/replicant4j/replicant","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replicant4j%2Freplicant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replicant4j%2Freplicant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replicant4j%2Freplicant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replicant4j%2Freplicant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/replicant4j","download_url":"https://codeload.github.com/replicant4j/replicant/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replicant4j%2Freplicant/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33808702,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-02T02:00:07.132Z","response_time":109,"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":["entity","gwt","java","network"],"created_at":"2024-11-12T04:05:52.693Z","updated_at":"2026-06-02T06:01:05.754Z","avatar_url":"https://github.com/replicant4j.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Replicant\n\n[\u003cimg src=\"https://img.shields.io/maven-central/v/org.realityforge.replicant/replicant-client.svg?label=latest%20release\"/\u003e](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.realityforge.replicant%22%20a%3A%22replicant-client%22)\n[![codecov](https://codecov.io/gh/replicant4j/replicant/branch/master/graph/badge.svg)](https://codecov.io/gh/replicant4j/replicant)\n\nThe replicant library aims to provide infrastructure for replicating a portion of a complex server-side\ndomain model to zero or more clients who have subscribed to the replication engine. When changes are\napplied on the server-side, the changes are batched and transmitted to interested clients. Upon receiving\nthe changes, the client will atomically apply the changes to a local client-side representation. The\napplication is then notified of the changes via a local message broker.\n\nThe library uses a client-side repository of objects, or replicas, that maintain the state of a subset of\nthe world. Changes are transmitted from the server to the client and the replicas are dynamically updated.\nWhen the replica's are updated, changes are propagated through to the user interface through the use of events\nand a centralized event broker. To avoid the scenario where the UI is updated when the repository is an\ninconsistent state, changes are applied in changesets and only when the complete changeset has been applied are\nthe changes propagated through the event broker.\n\nIt should be noted that replicant is designed to be integrated with other technologies, most notably\n[Domgen](https://github.com/realityforge/domgen), to provide a complete solution. It is most commonly\nused with an Java EE server component and a GWT front-end but it was originally derived from a client-server\nSwing application that used a custom application server.\n\n## Core Concepts\n\n### Entities and Replicas\n\nReplicant assumes that there is a client-side representation of the domain model. Each entity within the\nserver-side domain model that is to be replicated to the client-side should have a client-side entity\nthat mirrors the server-side model. The client-side representation or replica, need not be identical\nto the server-side model but one replica should map to one entity on the server. The replica may also\nomit attributes and relationships that are not needed on the client. The state and lifecycle of the\nreplica's will be managed by the replicant system.\n\nNB: It should be noted that as replicant was extracted and derived from several existing systems that\nused slightly different terminology, you may see terms such as _imitation_ used to refer to client-side\nentities or replicas. Over time these terms will be evolved out of the codebase and documentation.\n\n### Graphs and Subscriptions\n\nWhen a client connects to the replicant system, they are typically interested in a subset of the\ndata on the server; it is usually prohibitively expensive to transfer and store the entire server-side\ndomain model on the client. A more typical example is that a client wants to receive data about a subset\nof the domain model, for example they may query:\n\n* All payment classes.\n* All alerts within a 50km radius of coordinate X\n* All details about a particular vehicle or person\n* All data pertaining to a particular roster over a particular date range\n* etc.\n\nEach of these queries is represented as a graph within replicant. When a client subscribes to a graph,\nthe client will receive an initial message that contains the state of the world at the time of\nsubscription, that match the query. All subsequent changes to the world that match the query will be\npropagated to the subscribed clients until they unsubscribe or disconnect from the replicant system.\n\nThere are two major dimensions on which graphs are defined within the replicant system:\n* Is the graph a type graph or an instance graph?\n* Is the graph filtered or unfiltered?\n\n**Type Graphs**: A type graph is used when you want to replicate instances in the domain model based on\n the type of the entity. It is common for applications to place common reference data in type graphs,\n so that the entire set of reference data received in one message.\n\n**Instance Graphs**: An instance graph is used when you want to replicate details about a particular\n entity or root instance. All entities related to the root instance are considered to be part of the\n graph. An entity is related to the root instance if it references the root instance\n and the root instance can traverse to the entity. This relationship is transitive. For example, a\n `Person` entity may be referenced by the `Accreditation` entity, the `Accreditation` entity may be\n referenced by the `EvaluationResult` entity. If the `Person` entity is able to traverse to the\n `EvaluationResult` entity via the `Accreditation` entity then all three would be included in the\n instance graph rooted at a particular person. When the client subscribes to the Person graph with\n the root set to the person \"Bob\", then they will receive all of Bob's `Accreditation`s and all of\n Bob's `EvaluationResult`s. The developer may also explicitly shape and prune the graph so include\n or exclude entities from the instance graph.\n\n**Unfiltered Graph**: An unfiltered graph includes all entities in the _type graph_ or _instance graph_\n without further filtering.\n\n**Filtered Graph**: An filtered graph allows the developer to customize which entity instances are\n included in the graph. In the typical scenario where replicant is used in conjunction with domgen,\n the developer specifies which fields of which entities participate in the routing decision and the\n parameter that the client passes to the replicant engine to control the routing. Domgen then generates\n some template methods that the developer must implement to customize the subscription and routing\n capabilities.\n\nThere is several other features of graphs within the replicant engine, but these are typically used to\nmeet operational or system requirements. Two common features used in most replicant implementations are\ncacheable graphs and making filter parameters in filtered graphs immutable.\n\nA cacheable graph is used when the data within the graph has a relatively low frequency of change,\nthe volume of data is relatively large or the time to load the data from the database is relatively\nlong. If a graph is cacheable, then the client will store the entire graph in a client-side cache\nalong with a cache-key that supplied by the server. When the client re-requests that graph data, it\nsupplies the cache-key and the server can either indicate to the client should use the cached version\nor send a new version of the data contained within the graph.\n\nImmutable filter parameters indicate that it is not possible to update a parameter supplied during\nsubscription and that the client will need to unsubscribe and re-subscribe to change the parameter.\nFor example, if the graph for \"All alerts within a 50km radius of coordinate X\" has an immutable\nparameter for X, then the only way to change X is to unsubscribe from the graph and re-subscribe\nsupplying another value for parameter X. Immutable filter parameters are used to optimize routing\nand subscription mechanics.\n\nIt is possible and expected that one client may be subscribed to more than one graph and the graphs\nmay be overlapping. Often applications will be built such that one graph will link to another graph\nand automatically subscribe the client to the related graph.\n\nConsider a roster application. The developer may define one graph that includes assignment of people\nto activities on a single day. If the client was to subscribe to three days that shared people, then\nthe subscription would send the same people data down to the client multiple times. To avoid this the\ndeveloper can define another graph that contains details about people and **link** the day graph to\nzero or more person graphs.\n\nTODO: Insert diagram here\n\nIt is also possible to define multiple instance graphs for a single entity. For example there could be\none graph that includes a person and all their related accreditations, and another graph that includes\na person and all their related contact details.\n\nSome filtered graphs may support multiple instances of the same graph distinguished by a filter\ninstance id. In this case a client can subscribe to multiple instances of the same graph, where each\ninstance is routed independently by a filter instance id. These channels use instanced filter types\n(`DYNAMIC_INSTANCED` or `STATIC_INSTANCED`); only `DYNAMIC_INSTANCED` allows filter updates.\n\nThe codebase often refers to the \"Area of Interest\" or AOI of a client. This essentially indicates\nwhether an entity is contained within one of the graphs that client is subscribed to.\n\nNB: The codebase(s) for replicant map graphs to channels or data channels at the transport layer.\nThe identifier for the root entity in instance graphs is used to name a sub-channel. This is useful\nto understand when monitoring the communication between replicant clients and the replicant engines.\nWhen a graph uses instanced filters, the filter instance id is embedded into the channel descriptor\nafter a '#' suffix.\n\n### Services\n\nWithin the replicant system, it is expected that changes to entities occur on the server-side and\nare integrated with the replicant engine. The replicant client then has to make service calls to the\nserver-side to initiate changes. At the completion of the service call, the server component collects\nall changes that were made to the server-side entities during the service call and passes them to the\nreplicant engine. The replicant engine is then responsible for replicating changes out to the interested\nclients.\n\nThe service infrastructure within replicant is such that it is possible to treat services as either;\n\n**fire and forget**: The client does not need to be notified when the service call completes.\n\n**immediate return**: The client is notified when the service call returns, potentially receiving a result\n from the server. Any changes made to entities on the service _may not_ be present on the client.\n\n**return when complete**: The client is notified when the service completes, potentially receiving a result\n from the server. Any changes made to entities on the service _must_ be present on the client.\n\n### Change Notifications\n\nChanges are replicated out to the clients in Change Sets. Each change set typically represents a unit\nof work, transaction or a single service call on the server-side. So all changes that occur within\na single transaction are routed and packaged as a single change set when sent to the client. The change\nset is then applied atomically to the client-side replication. This is an attempt to provide some consistency\nguarantees around the client-side representation.\n\nAfter a change set is applied a `DataLoadComplete` message is fired on the client-side. To get fine-grain\nnotification of changes, the developer can register listeners on the client-side broker and receive\nnotification when an entity is added, removed or updated. This is only possible when the change set is\nmarked as an _incremental load_ rather than as a _bulk load_. The vast majority of service calls result\nin _incremental load_ change sets, but sometimes for the sake of performance subscribe service calls and\nother calls that result in mass change may result in _bulk load_ change sets.\n\n### Server-Side Broker Scheduling\n\nOn the server, `ReplicantMessageBrokerImpl` queues pending packets on the target `ReplicantSession` and\nsubmits demand-driven drain tasks to the container-managed scheduled executor. The broker coalesces work by\nsession, so one session is processed by at most one drain task at a time, and hot sessions are yielded after\na bounded packet batch so other sessions can make progress.\n\nThe broker reads optional component environment entries from `java:comp/env` during startup:\n\n* `replicant/broker/maxConcurrentDrainTasks`: maximum number of concurrent drain tasks. Defaults to\n  `max(2, Runtime.getRuntime().availableProcessors())`.\n* `replicant/broker/maxPacketsPerRun`: maximum packets processed for one session claim. Defaults to `64`.\n* `replicant/broker/maxSessionsPerDrainTask`: maximum session claims processed by one drain task. Defaults\n  to `64`.\n\nEach value must be at least `1`. Missing entries use the defaults. Values may be numeric JNDI entries or\nnumeric strings; non-numeric strings, wrong types, and values below `1` fail startup.\n\n## Client-Side Developer Components\n\nThere are several replicant components that developers directly interact with in client-side code.\n\n### EntitySubscriptionManager\n\nThe `EntitySubscriptionManager` records the state of subscriptions on the client-side. This includes\nwhich graphs are subscribed two and the mapping between client-side replicas and the graph(s) that\ncaused the replica to be replicated to the client. The subscription state is typically managed by the\nserver-side but it is not uncommon for clients to query which subscriptions state.\n\n# History\n\nReplicant is derived from several existing implementations of this strategy. It was initially based on code\nextracted from a client-server Swing application and a client-server game server. However it is predominantly\nused in GWT/HTML applications in it's current incarnation. Replicant also incorporates some ideas from\n[HLA](http://en.wikipedia.org/wiki/High-level_architecture_\\(simulation\\)) extracted from a research project\nconducted during the completion of a PhD.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freplicant4j%2Freplicant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freplicant4j%2Freplicant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freplicant4j%2Freplicant/lists"}