{"id":24247258,"url":"https://github.com/dncomponents/dnnorthwind","last_synced_at":"2025-08-01T19:04:47.247Z","repository":{"id":239130063,"uuid":"350083648","full_name":"dncomponents/dnnorthwind","owner":"dncomponents","description":"Java web application to demonstrate capabilities of dncomponents","archived":false,"fork":false,"pushed_at":"2023-03-26T15:52:03.000Z","size":1065,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-05T17:23:41.667Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dncomponents.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2021-03-21T18:24:51.000Z","updated_at":"2024-10-17T09:09:16.000Z","dependencies_parsed_at":"2024-05-10T07:45:59.938Z","dependency_job_id":"4a53510e-2c7b-4fd8-847b-3a5265ff3ff8","html_url":"https://github.com/dncomponents/dnnorthwind","commit_stats":null,"previous_names":["dncomponents/dnnorthwind"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dncomponents/dnnorthwind","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dncomponents%2Fdnnorthwind","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dncomponents%2Fdnnorthwind/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dncomponents%2Fdnnorthwind/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dncomponents%2Fdnnorthwind/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dncomponents","download_url":"https://codeload.github.com/dncomponents/dnnorthwind/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dncomponents%2Fdnnorthwind/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268281606,"owners_count":24225157,"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-08-01T02:00:08.611Z","response_time":67,"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":[],"created_at":"2025-01-14T23:32:29.431Z","updated_at":"2025-08-01T19:04:47.209Z","avatar_url":"https://github.com/dncomponents.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Dncomponents SpringBoot Example\n\nThe goal of the project is to:\n\n1) Demonstrate Dncomponents UI library capabilities\n2) Show advantages of MVP pattern and elemental 2 browser API.\n3) Show how to combine other js libraries using jsinterop (in this case Chart.js)\n4) Give instructions for using DTOs as a lightweight choice for communication with server\n5) Promote usage of java language as a frontend choice.\n\nThe project uses The Northwind Microsoft database.\n\n### Technology Stack\n\nThe project is created with [tbroyer archetype](https://github.com/tbroyer/gwt-maven-archetypes)\nand its [spring version](https://github.com/NaluKit/gwt-maven-springboot-archetype) . It is fully J2CL compatible. Only\njava language and elemental2 library.\n\n***Frontend***\n\n* [dncomponents](https://dncomponents.com/index.html) UI library with\n* [bootstrap 5](https://getbootstrap.com/) css framework\n* [Intellij dncompononents plugin](https://plugins.jetbrains.com/plugin/13486-dn-components)\n* [GWT compiler](http://www.gwtproject.org/)\n* [elemental2](https://github.com/google/elemental2) java browser API library.\n* [Chartjs](https://www.chartjs.org/)\n\n***Backend*** _(REST)_\n\n* [SpringBoot](https://projects.spring.io/spring-boot)\n* [H2 In Memory database](https://www.h2database.com/html/main.html)\n* [Spring data JPA](https://spring.io/projects/spring-data-jpa)\n* [Model mapper](http://modelmapper.org/)\n* [Maven build tool](https://maven.apache.org/)\n\n\u003cdiv style=\"width:1200px\"\u003e\n\n![p2](screenshots/p2.png)\n\n\u003c/div\u003e\n\n### MVP and separation of concern\n\nDncomponents is devoted to Separation of concerns principle, and has been built upon it. Proper use of MVP pattern is\ncrucial for good design of web applications. This project demonstrates use of MVP pattern with help\nof [Intellij dncompononents plugin](https://plugins.jetbrains.com/plugin/13486-dn-components). You can create a group of\nActivity, Place, View, Presenter and ViewImpl classes: right click folder _\"File-\u003eNew-\u003edn-components-\u003ecreate\nview/presenter/place group\"_.\n\nYour HTML code goes naturally to html files which keeps java code clean and\nneat. [HTML binder](https://dncomponents.com/documentation-html-binder.html) and dncomponents plugin makes building user\ninterface pleasant experience.\n\n\u003cdiv style=\"width:1200px\"\u003e\n\n**html file**\n\n![i1](screenshots/idea1.png)\n\n**Java counterpart**\n\n![i2](screenshots/idea2.png)\n\n\u003c/div\u003e\n\n### Javascript integration\n\nThanks to `JsInterop`, integration with javascript is simple. E.g. in this project we use Chartjs to show vertical bar\nchars. Create .js file and place it at a public folder add this js code:\n\n```javascript\nfunction showStatsJs(canvas, a, label, color, title) {\n    const names = a.map(x =\u003e x.NAME);\n    const values = a.map(x =\u003e x.VALUE);\n    const chart = new Chart(canvas, {\n        type: 'bar',\n        data: {\n            labels: names,\n            datasets: [{\n                label: label,\n                data: values,\n                backgroundColor: color\n            }]\n        },\n        options: {\n            scales: {\n                yAxes: [{\n                    ticks: {\n                        beginAtZero: true\n                    }\n                }]\n            },\n            title: {\n                display: true,\n                text: title\n            }\n        }\n    });\n}\n\n```\n\nFrom java code call this function:\n\n```java\n    @JsMethod(namespace = GLOBAL)\npublic static native void showStatsJs(HTMLCanvasElement canvas,JsObject[]a,String label,String color,String title);\n```\n\n\u003cdiv style=\"width:1200px\"\u003e\n\n![p1](screenshots/p1.png)\n\n\u003c/div\u003e\n\n### DTO rules\n\n_(note: DTO rules are inspired\nby [JsInterop DTO strategy](https://github.com/intendia-oss/autorest/wiki/JsInterop-DTO-strategy) but it's different,\neven opposite at some elements. However, if it fits to your needs you can use it too)_\n\nFor transferring objects use plain javascript objects using JsInterop `native @JsType`. With following rules:\n\n1) Fields should be always package protected! (because they can't be private) Practically we consider them private.\n   Never access them directly - use getters and setters methods annotated with `@JsOverlay`.\n\n2) For any java number type, always use Double field except for Long and BigInteger where you should use string field!\n   Then type conversion happens through getters and setters methods. For Collections use array field. Always use boxed\n   java number types. (Double perfectly fits as a field for numbers. It returns **null** for both **undefined** and **\n   null** values. Primitive types are bad in this case because e.g. _**int**_ can't be **null** but can be **undefined**\n   .)\n\n3) When transferring objects from server to client always serialize and deserialize only **fields**! Never use getters\n   and setters methods. E.g. by default Jackson uses both fields and getters and setters methods for serialization. In\n   spring boot you can set this rule on global level like this:\n\n```java\n// If you use Spring Boot, you can configure Jackson globally as follows:\n@Configuration\npublic class JacksonObjectMapperConfiguration implements Jackson2ObjectMapperBuilderCustomizer {\n\n    @Override\n    public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {\n        jacksonObjectMapperBuilder.visibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);\n        jacksonObjectMapperBuilder.visibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE);\n        jacksonObjectMapperBuilder.visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);\n    }\n}\n```\n\nDon't consider getters and setters property methods. We want to transfer only fields!\n\n4) It is highly recommended wrapping each DTO object to its pure java wrapper class. The reason is that on a client side\n   native JsTypes are not proper Java classes (In GWT terms - nothing of Java object - no equals methods, hashcode,\n   toString, instance of...). On server side DTOs are regular java objects. Creating Wrapper classes might look as a\n   superfluous step but creating and using them isn't hard at all. Follow this simple rules for Wrapper classes:\n\n#### DTO Wrapper rules\n\n1) Add a private field of DTO type with empty constructor that initialise dto and constructor with dto argument.\n\n2) Delegate all methods from DTO field.\n\n   Don't do this manually, kindly ask your IDE to do this for you. With Intellij: `ctr+enter -\u003e Delegate Methods...`\n\n3) Override equals and hashCode methods\n\n   Usually id from dto object is used for creating equals and hashcode methods.\n\n4) Create _wrap_ and _unWrap_ methods\n\n5) If any getters and setters method use other DTO objects, correct them to use its Wrapper version!\n\n6) On client side transform DTO objects to its wrappers and vice versa when sending back to server.\n\ne.g. Array OrderDTO to List of OrderDTOWrapper\n\n```java\n\nOrderDTO[]orders\n\nprivate List\u003cOrderDTOWrapper\u003e orderDTOS=Arrays.asList(orders).stream()\n        .map(o-\u003eo.wrap())\n        .collect(Collectors.toList());\n```\n\n#### OrderDTO\n\n```java\n\n@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = \"Object\")\npublic class OrderDTO {\n\n    Double order_id;\n    CustomerDTO customer;\n    Double order_date;\n    OrderDetailDTO[] orderDetails;\n\n    public final @JsOverlay\n    Integer getOrder_id() {\n        return order_id == null ? null : order_id.intValue();\n    }\n\n    public final @JsOverlay\n    void setOrder_id(Integer order_id) {\n        this.order_id = order_id == null ? null : Double.valueOf(order_id);\n    }\n\n    public final @JsOverlay\n    CustomerDTO getCustomer() {\n        return customer;\n    }\n\n    public final @JsOverlay\n    void setCustomer(CustomerDTO customer) {\n        this.customer = customer;\n    }\n\n    public final @JsOverlay\n    Date getOrder_date() {\n        return order_date == null ? null : new Date(order_date.longValue());\n\n    }\n\n    public final @JsOverlay\n    void setOrder_date(Date order_date) {\n        this.order_date = order_date == null ? null : (double) order_date.getTime();\n    }\n\n    public final @JsOverlay\n    List\u003cOrderDetailDTO\u003e getOrderDetails() {\n        return Arrays.asList(orderDetails);\n    }\n\n    public final @JsOverlay\n    void setOrderDetails(List\u003cOrderDetailDTO\u003e orderDetails) {\n        this.orderDetails = orderDetails.toArray(new OrderDetailDTO[0]);\n    }\n\n    public final @JsOverlay\n    OrderDTOWrapper wrap() {\n        return new OrderDTOWrapper(this);\n    }\n\n}\n```\n\n#### OrderDTOWrapper\n\n```java\npublic class OrderDTOWrapper {\n\n    //dto delegate methods\n    private OrderDTO orderDTO;\n\n    public OrderDTOWrapper(OrderDTO orderDTO) {\n        this.orderDTO = orderDTO;\n    }\n\n    public OrderDTOWrapper() {\n        this.orderDTO = new OrderDTO();\n    }\n\n    //override equals\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        OrderDTOWrapper that = (OrderDTOWrapper) o;\n        return this.getOrder_id().equals(that.getOrder_id());\n    }\n\n    @Override\n    public int hashCode() {\n        return this.getOrder_id().hashCode();\n    }\n\n    //refer to wrapper version of DTO object\n    public CustomerDTOWrapper getCustomer() {\n        return orderDTO.getCustomer().wrap();\n    }\n\n    public void setCustomer(CustomerDTOWrapper customer) {\n        orderDTO.setCustomer(customer.unWrap());\n    }\n\n    public Integer getOrder_id() {\n        return orderDTO.getOrder_id();\n    }\n\n    public void setOrder_id(Integer order_id) {\n        orderDTO.setOrder_id(order_id);\n    }\n\n    //list of DTOs mapped to wrapper class\n    public List\u003cOrderDetailDTOWrapper\u003e getOrderDetails() {\n        return orderDTO.getOrderDetails().stream()\n                .map(o -\u003e o.wrap())\n                .collect(Collectors.toList());\n    }\n\n    public void setOrderDetails(List\u003cOrderDetailDTOWrapper\u003e orderDetails) {\n        orderDTO.setOrderDetails(orderDetails.stream()\n                .map(o -\u003e o.unWrap())\n                .collect(Collectors.toList())\n        );\n    }\n\n    //Unwrap\n    public OrderDTO unWrap() {\n        return orderDTO;\n    }\n}\n\n```\n\n### Run Application\n\n```bash\n# From the root directory run commands: \n\n# In one terminal window: \nmvn gwt:codeserver\n\n# In another terminal window: \nmvn spring-boot:run\n\n# open:\nhttp://localhost:8080\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdncomponents%2Fdnnorthwind","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdncomponents%2Fdnnorthwind","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdncomponents%2Fdnnorthwind/lists"}