From 6b908738362854c2b984f0ea0b9c269501261b1b Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 23 Jan 2023 17:54:09 +0100 Subject: [PATCH 01/52] Added reactive-programming and why-semantic-tech --- docs/explanations/reactive-programming.md | 36 +++++++++++++++++++++++ docs/explanations/why-semantic-tech.md | 16 ++++++++++ 2 files changed, 52 insertions(+) create mode 100644 docs/explanations/reactive-programming.md create mode 100644 docs/explanations/why-semantic-tech.md diff --git a/docs/explanations/reactive-programming.md b/docs/explanations/reactive-programming.md new file mode 100644 index 0000000..f5ca1ea --- /dev/null +++ b/docs/explanations/reactive-programming.md @@ -0,0 +1,36 @@ +# Reactive programming +We are experimenting with reactive programming. Why? Orchestration! + +The traditional mu.semte.ch architecture provides user-facing microservices. The frontend orchestrates the microservices as it is best suited to communicate their effects to the end-user. But what about backend microservices? How do we let those communicate? Can we indicate to the user where things were left off? Yes. Yes we can. With reactive programming. + +A drawing of a scientist experimenting with RDF + +With reactive programming, our services respond to a certain state being available in the database. As the state changes, the service is informed, and it can react accordingly. An email service could detect that an email is currently in the Outbox, mail it to the right user, and move it to the Sent box. We can keep the database as the only sync-point and have services start tasks based on other services’s work. Backend services can communicate without direct dependencies, using triples to describe their state in the triplestore. The end-user can be informed on the process by visualizing this state in the frontend. + +## An email example +Backend services write contents to the triplestore, which is discovered by other microservices hooking into this content. + +Let’s assume an email system. As the user creates an email, it is in Draft status. Once the email should be sent, we move that email into the Outbox. As this email gets connected to the Outbox, the email sending service picks it up. It sends the email, and moves it to the Sent mailbox. Each of these states is trivially easy to express in the semantic model. + +## Embracing failure +It may be that a microservice drops out. Perhaps we ran out of emails to send under our current plan, perhaps the server hosting our server decided to go on a holiday. Our approach ensures that, once the service gets back up, it picks up the work from where it left off. + +Assume we prepare 5 emails to be sent at once. We place each of the emails in the Outbox, and the sending service starts mailing. As it has sent the third email, we kill the service. Two emails are left unsent. They are still present in the Outbox. When we restart our mailer-service, it checks what emails are still in the Outbox. The two mails that match are sent. As new emails arrive, the mailer picks them up and sends them. + +Failures can happen. It is important to ensure the failure of a single microservice doesn’t bring down the whole application. A big win for reactive microservices. + +## Rich combinations +Reactive programming can make the construction of an application a lot simpler. As we inform the user about state changes, an understanding of the system as a whole can be supplied. + +Let us consider a complex mailing and tagging system. As we draft an email, we see it in the Draft box. When we send it, our service moves it to the Outbox. Our user interface reflects this change, and shows the email in the Outbox now. The mailer service picks up our email, sends it, and moves it to the Sent box. This too, can be reflected in the user interface. This whole picture makes it simple for the user to understand what is going on in the application. + +We can easily push the boundaries for these rich microservices further with more complex systems. For instance, we may extend our mail client with automatic tagging of emails. As we classify a set of emails, a Neural Network service indicates that it has started training on the new examples. Our user interface shows this state. Once the network has been trained, the updated parameters are written to the store. Our classification interface hooks into this to launch a new auto-classification of emails we haven’t checked yet. The microservices are fully decoupled, and the user can easily get a grip on the fairly complex set of operations going on in the backend. + +Rich applications are easier to construct when clear boundaries exist and are being communicated about. + +## What's next? +We have ran advanced experiments and have PoCs of the necessary tooling for reactive programming on the mu.semte.ch stack. + +The [delta-service](https://github.com/mu-semtech/mu-delta-service) calculates triples that would be changed by the execution of an update query. It can inform other services about these changes so they can update as necessary. We are building some example microservices leveraging this approach, whilst applying the concept to end-user PoC applications. + +*This document has been adapted from Aad Verstend's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/02/09/reactive-programming/)* diff --git a/docs/explanations/why-semantic-tech.md b/docs/explanations/why-semantic-tech.md new file mode 100644 index 0000000..8055eac --- /dev/null +++ b/docs/explanations/why-semantic-tech.md @@ -0,0 +1,16 @@ +### Why semantic tech? (The future of web apps is mashed up!) +Interactive websites have been the norm for a few years now. Simple contact forms don’t seal the deal anymore. We need logins, [Instagram](https://www.instagram.com/) integration and instant [Twitter](https://twitter.com/) notifications. It’s time to turbocharge our web development to match the new demand. Single page web applications, mashed up from various services, will push developer productivity to a whole new level. + +Take the example of a complaint form with an image upload. You probably want the upload to work smoothly, to start the upload eagerly. You may want to show an image preview of the uploaded file. Allow drag and drop, maybe? All that new shiny stuff. But let’s take a step back here. Is image upload really your core business? No? Then why would you develop it? + +a drawing of someone using a masher + +The future of web applications is mashed up apps. Single page apps that use a multitude of services which are readily available, or which are easy to deploy on your own premises. The app orchestrates the used services so the application works as expected. No project-specific development of components which aren’t core to your business. You can use [Firebase](https://firebase.com/) to store the data of your app, take pictures from [Flickr](https://www.flickr.com/), and stream videos from [YouTube](https://www.youtube.com/). Maybe you’ll still have an in-house service for some custom business logic, but that will be limited. We should take the sharing of open source code to a new level, from software libraries, to microservices. Web apps can use these services, whether they’re hosted at your own premises, or offered as an external service. + +There’s more to web development than the backend though. New JavaScript technologies, like [ES6 modules](http://www.ecma-international.org/ecma-262/6.0/#sec-modules) or even [webcomponents](http://webcomponents.org/), make it easier than ever to share code. Although browsers may not fully support them yet, big web development frameworks [already](https://guides.emberjs.com/v2.3.0/) [support](https://www.polymer-project.org/1.0/) [much](https://github.com/klaemo/react-es6) [of](https://github.com/gocardless/es6-angularjs) [it](https://guides.emberjs.com/v2.3.0/components/defining-a-component/). The problems of the frontend are similar to those of the backend. Why would you develop the code to talk to Flickr? Why would you implement the login form for each of your projects? We can share it, plug it in our app, and extend it to match our needs. By sharing whole components in the backend and in the frontend, we get a clear feel for the final product long before it has been polished. Most of the time spent will be time spent on polishing the app. We’re looking forward to the new paradigms of software development. + +And this isn’t the end stage yet. Standardization efforts mark a new way of development. JSON requests are becoming standardised using [JSONAPI](http://jsonapi.org/), allowing us to automatically build the glue code between the frontend and the backend. CSS3 marks major changes in terms of [what](https://developer.mozilla.org/en-US/docs/Web/CSS/filter) [we](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Using_CSS_flexible_boxes) [can](https://drafts.csswg.org/css-backgrounds-3/#border-radius) [visualise](https://drafts.csswg.org/css-transitions/), [websockets](http://www.html5rocks.com/en/tutorials/websockets/basics/) will allow for more interactive applications, [web storage](https://www.w3.org/TR/webstorage/#the-localstorage-attribute) has been standardised, [web](http://www.html5rocks.com/en/tutorials/workers/basics/) [workers](https://www.w3.org/TR/workers/) allow for multithreaded applications and there’s a whole slew of standards which we refuse to mention here because we are already mentioning too many shiny new things! + +Mashup-like architectures will make developing new applications easier and less time-consuming. It’s a logical architectural change driven by the evolution in standards, frontend frameworks, and backend automation. We’re jumping on the train with [mu.semte.ch](http://mu.semte.ch/), and you’re free to take a ride with us. + +*This document has been adapted from Aad Verstend's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/01/14/the-future-of-web-apps-is-mashed-up/)* From 645a3a16c09741aa095ca6e5667746a00cc46f7f Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 23 Jan 2023 18:26:07 +0100 Subject: [PATCH 02/52] Added experimentation, fixed Aad's name (sorry) --- docs/explanations/experimentation.md | 39 +++++++++++++++++++++++ docs/explanations/reactive-programming.md | 2 +- docs/explanations/why-semantic-tech.md | 2 +- 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 docs/explanations/experimentation.md diff --git a/docs/explanations/experimentation.md b/docs/explanations/experimentation.md new file mode 100644 index 0000000..6a98bfe --- /dev/null +++ b/docs/explanations/experimentation.md @@ -0,0 +1,39 @@ +# Experimentation (All or nothing fails to innovate) + +Introducing new technologies is hard.  Very hard.  No matter how promising the technology is, the amount of unknowns is daunting.  Who knows what the hidden risks of [Ballerina](http://ballerinalang.org/) will be in practice? The price of technical innovation is too damn high.  mu.semte.ch makes experimentation easy, and cheap. + +[![The price of innovation is too damn high -- made at imgflip.com](https://i.imgflip.com/1k6pqj.jpg)](https://imgflip.com/i/1k6pqj) + +## Monetheism is not your future + +Hind-sight 20/20.  As easy as it is to see what turned out to be the best way, it is hard to figure out what will turn out ok in the future.  Should you guess on building your stack on the über-efficient [Elixir](http://elixir-lang.org/) language?  Will you find the devs?  How about [Haskell](https://www.haskell.org/)?  How about just taking the safe bet that you’ll find enough devs tackling the problem in [Java](https://java.com)?  It is no easy choice. + +It is hard to find good developers.  In any language.  But it seems that some rock-star developers have found the holey grail in one or another strange language.  It happened with [Ruby](https://www.ruby-lang.org) as [Ruby on Rails](http://rubyonrails.org/) became big.  Some great thinkers swear by the simplicity of Haskell, others by the speed of Elixir.  We can spend decades arguing our choice is the best.  There is a catch though, we do tend to conclude that all options have their merits.  Most options have their own reason to exist.  The best solution simply depends on your problem. + +When we look at specific tasks, the problem of choosing a programming language shifts from a religious war of _“the best programming language ever”_ to _“the best language for the problem”_.  A much simpler question, with a much more reasonable answer.  Need to tackle many long-running requests?  Better pick Elixir.  Want to see how purely functional programming would work in practice?  Use Haskell.  Do you think it’d be easier if the backend and the frontend used one language?  Javascript is your friend.  In the holy war of programming languages, monotheism is dead. + +As much as I’d personally like for us to agree that [Common Lisp is sent to us from the gods](https://www.youtube.com/watch?v=5-OjTPj7K54), I’ve learned that most others don’t agree.  It has its place, but betting on it for the sake of your company is an HR suicide mission.  mu.semte.ch uses microservices, all wrapped in their [Docker](https://www.docker.com/) container, meaning they can all choose their programming language.  For an innovation-driven company, the hard choice is figuring out a minimal set of hard constraints on the implementation, so a wide array of unknown choices can be made in the future, without causing extra friction. + +It is not an easy choice to pick the right programming language, so why make it?  Making the choice stifles innovation.  It is not to be made. + +## The lack of priests + +Clearly, there are downsides of having loads of programming languages in your active code-base.  A single language brings benefits.  There must be a balancing act to be made. + +As everyone can introduce new programming languages, how do you keep an oversight over the space?  There is an inherent risk accompanied in using a microservice in a language you don’t know.  Or, god forbid, the lone Haskell developer leaving your company.  Fear not, these are not the risks you are looking for. + +*I don’t understand a microservice in a strange language.* A common worry would be that developers have problems fixing an application in a strange programming language.  This can be true for complex applications, but small microservices are much easier to understand.  mu.semte.ch augments this with templates, offering basic constructs with similar naming in a variety of programming languages.  This makes it easier to dive into a piece of code you’ve never seen, and understand what is going on.  Worst case, it’s a microservice.  You take ownership and rewrite it in your own language of choice. + +*How do I adapt a microservice in a strange language?* Each microservice should be configurable.  It turns out that basic configuration can happen through environment variables.  Meaning it’s exactly the same, regardless of the underlying programming language.  More complex configuration may need to happen in the common method for the specific language.  In Ruby, YAML is common to be used.  In Lisp, S-expressions can be used.  Regardless, the syntax of the configuration is contained in examples in the sources or the README, you just follow that syntax. + +*What if my rock-star Ballerina dev leaves?* It is supposed to be microservice.  The hard part is figuring out what the service should entail exactly.  For the large majority of the services, it holds that they can be rewritten in a matter of days, given knowledge on the requests that will come in.  Fact of the matter is, that a motivated dev is way faster than a bored one.  It’s better to keep using the microservice of your rock-star dev, and replace it when substantial changes need to be made. + +There are other risks on using many programming languages.  There must be.  But they are greatly overshadowed by the benefits.  Namely the motivation and efficiency of the developers turning your company strategies into an automated version of reality. + +## An all-inclusive playground + +All or nothing fails to innovate.  We should start embracing the new kids on the block.  Languages, or frameworks, with a limited track-record could be of great benefit.  I will give Ballerina a swirl, a language released just a few days ago.  I’ll test run it to appreciate its benefits, and discover its shortcomings.  For a virtually non-existent cost, I will have replaced an existent microservice in a real-world application. + +Diversity is what makes all ecosystems thrive.  It is time to apply it to your company. + +*This document has been adapted from Aad Versteden's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/02/23/all-or-nothing-fails-to-innovate/)* diff --git a/docs/explanations/reactive-programming.md b/docs/explanations/reactive-programming.md index f5ca1ea..0b2c2db 100644 --- a/docs/explanations/reactive-programming.md +++ b/docs/explanations/reactive-programming.md @@ -33,4 +33,4 @@ We have ran advanced experiments and have PoCs of the necessary tooling for reac The [delta-service](https://github.com/mu-semtech/mu-delta-service) calculates triples that would be changed by the execution of an update query. It can inform other services about these changes so they can update as necessary. We are building some example microservices leveraging this approach, whilst applying the concept to end-user PoC applications. -*This document has been adapted from Aad Verstend's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/02/09/reactive-programming/)* +*This document has been adapted from Aad Versteden's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/02/09/reactive-programming/)* diff --git a/docs/explanations/why-semantic-tech.md b/docs/explanations/why-semantic-tech.md index 8055eac..f3dd655 100644 --- a/docs/explanations/why-semantic-tech.md +++ b/docs/explanations/why-semantic-tech.md @@ -13,4 +13,4 @@ And this isn’t the end stage yet. Standardization efforts mark a new way of de Mashup-like architectures will make developing new applications easier and less time-consuming. It’s a logical architectural change driven by the evolution in standards, frontend frameworks, and backend automation. We’re jumping on the train with [mu.semte.ch](http://mu.semte.ch/), and you’re free to take a ride with us. -*This document has been adapted from Aad Verstend's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/01/14/the-future-of-web-apps-is-mashed-up/)* +*This document has been adapted from Aad Versteden's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/01/14/the-future-of-web-apps-is-mashed-up/)* From cfe2cbef3f909cc18456511ba0a15c882a885439 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 23 Jan 2023 20:01:13 +0100 Subject: [PATCH 03/52] Imported oslo2 writeup --- writeups/oslo2.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 writeups/oslo2.md diff --git a/writeups/oslo2.md b/writeups/oslo2.md new file mode 100644 index 0000000..10d2996 --- /dev/null +++ b/writeups/oslo2.md @@ -0,0 +1,52 @@ +# (Lessons learned from) OSLO² + +[Dacota.one](http://dacota.one) used mu.semte.ch to publish [CRAB-LOD as Linked Open Data](http://data.vlaanderen.be/doc/adres/2179183). + +Publishing linked open data is not at the core of the mu.semte.ch stack, yet the choice makes sense. As Linked Data is the core data model in mu.semte.ch to connect the microservices, all necessary information is available. + +In the light of publishing Linked Open Data correctly, we should publish the contents in various ways. One of the ideal methods of publication are the subject pages.  These pages provide a human view on the rendered contents. + +Following is a set of lessons learned on using the mu.semte.ch stack for publishing Linked Open Data. + +## Frontend generators + +[OSLO²](https://overheid.vlaanderen.be/producten-diensten/OSLO2) uses mu-cl-resources to generate the API of the resources.  In this case [addresses, streets and municipalities](https://overheid.vlaanderen.be/producten-diensten/adressenregister-crab).  The content of the subject pages is everything specified in the mu-cl-resources configuration. This means we can derive the contents of the subject pages by interpreting the mu-cl-resources configuration. + +Based on a core idea of generated pages on mu.semte.ch, similar to the [generators in Rails,](http://guides.rubyonrails.org/generators.html) the CRAB-LOD team extended our PoC efforts.  The core benefit of generating the pages is that they are adaptable.  The generators construct views which you can alter as needed, giving you a fast starting point.  The generators are handy when adding new fields, you can overwrite the generated contents.  But you can also override the general feel of the website whilst looking at the real data to publish, thereby making large style changes easier.  By materialising the generated views, customisation becomes easier.  Not all of these features were needed by CRAB-LOD, but it allows the approach to be used in more places. + +The approach of using frontend generators worked fine.  We will further clean up the repositories and document the approach for future use. + +## Content negotiation + +There are multiple ways in which we may share a piece of information.  Content negotiation allows the browser to request the content in the way he wants it shared. + +Based on the type of content the user requests, we should be able to dispatch to a different microservice.  One service may be responsible for hosting content as triples, another as a json-api. The mu-dispatcher does not support this.  In order to make the platform better suited for Linked Data publishing, we should extend the dispatcher with content negotiation capabilities. + +We are strongly considering this extension to the mu-dispatcher. + + +## Read-only query optimisation + +There are no live updates to the data in OSLO².  As such, many database queries can be cached, relieving the triplestore and rendering the pages more swiftly. + +SPARQL queries can be sent as HTTP requests, which is the method we use for our microservices.  These requests are easy to cache.  Slight problem is that the requests are HTTP POST requests, which by HTTP semantics are not supposed to be cached.  This is correct, the contents of the triplestore may change and should thus, in the traditional interpretation, not be cached. + +Bert Van Nuffelen built [a proxy microservice which caches the HTTP POTS requests to the triplestore](https://github.com/bertvannuffelen/SimpleSparqlCache).  Given a static database, like CRAB-LOD, this removes stress from the triplestore.  We have not ran benchmarks yet, but the API feels much more snappy this way. + +## Linked Data Fragments + +In a short wrap-up discussion with Pieter Colpaert on the subject, [Linked Data Fragments](http://linkeddatafragments.org/) (LDF) were pushed forward.  Although not connected to CRAB-LOD yet, it is a method of publishing Linked Data. + +Given the brief discussion with Pieter, it appears to be easy to add this method of publishing Linked Open Data to the outside world.  LDF can be easy on the triplestore and makes federated SPARQL querying the default.  Check their [website](http://linkeddatafragments.org/) for more information. + +We are looking into making Linked Data Fragments a drop-in addition to the framework.  Thereby giving you a free and promising extra way of sharing your contents with the world. + +## Conclusion + +The mu.semte.ch stack has proven to be a nice way of publishing Linked Data. + +Given content available in the triplestore, manipulation is easy.  Performance is swift.  And with the generated subject pages, both adding content, as well as customising views when necessary is feasible.  With the integrated application, it is possible to add a different look and feel to the frontend, whilst still having much reuse. + +It is nice to be used at an organisation like the Flemish Government and expect similar projects to appear in the future. + +*This document has been adapted from Aad Versteden's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/04/06/lessons-learned-from-oslo%c2%b2/)* From bb26b84c08a4fd0069962274bf3086ec0108fd11 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Tue, 24 Jan 2023 07:26:20 +0100 Subject: [PATCH 04/52] Renamed explanations to discussions --- docs/{explanations => discussions}/experimentation.md | 0 docs/{explanations => discussions}/reactive-programming.md | 0 docs/{explanations => discussions}/why-semantic-tech.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename docs/{explanations => discussions}/experimentation.md (100%) rename docs/{explanations => discussions}/reactive-programming.md (100%) rename docs/{explanations => discussions}/why-semantic-tech.md (100%) diff --git a/docs/explanations/experimentation.md b/docs/discussions/experimentation.md similarity index 100% rename from docs/explanations/experimentation.md rename to docs/discussions/experimentation.md diff --git a/docs/explanations/reactive-programming.md b/docs/discussions/reactive-programming.md similarity index 100% rename from docs/explanations/reactive-programming.md rename to docs/discussions/reactive-programming.md diff --git a/docs/explanations/why-semantic-tech.md b/docs/discussions/why-semantic-tech.md similarity index 100% rename from docs/explanations/why-semantic-tech.md rename to docs/discussions/why-semantic-tech.md From bf38b010eb17af3484363b912c0213af96c0d822 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Tue, 24 Jan 2023 07:32:27 +0100 Subject: [PATCH 05/52] Imported mu.semte.ch as the ideal playground --- docs/discussions/experimentation.md | 47 ++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/docs/discussions/experimentation.md b/docs/discussions/experimentation.md index 6a98bfe..b7594d2 100644 --- a/docs/discussions/experimentation.md +++ b/docs/discussions/experimentation.md @@ -1,10 +1,12 @@ -# Experimentation (All or nothing fails to innovate) +# Experimentation + +## All or nothing fails to innovate Introducing new technologies is hard.  Very hard.  No matter how promising the technology is, the amount of unknowns is daunting.  Who knows what the hidden risks of [Ballerina](http://ballerinalang.org/) will be in practice? The price of technical innovation is too damn high.  mu.semte.ch makes experimentation easy, and cheap. [![The price of innovation is too damn high -- made at imgflip.com](https://i.imgflip.com/1k6pqj.jpg)](https://imgflip.com/i/1k6pqj) -## Monetheism is not your future +### Monetheism is not your future Hind-sight 20/20.  As easy as it is to see what turned out to be the best way, it is hard to figure out what will turn out ok in the future.  Should you guess on building your stack on the über-efficient [Elixir](http://elixir-lang.org/) language?  Will you find the devs?  How about [Haskell](https://www.haskell.org/)?  How about just taking the safe bet that you’ll find enough devs tackling the problem in [Java](https://java.com)?  It is no easy choice. @@ -16,7 +18,7 @@ As much as I’d personally like for us to agree that [Common Lisp is sent to u It is not an easy choice to pick the right programming language, so why make it?  Making the choice stifles innovation.  It is not to be made. -## The lack of priests +### The lack of priests Clearly, there are downsides of having loads of programming languages in your active code-base.  A single language brings benefits.  There must be a balancing act to be made. @@ -30,10 +32,45 @@ As everyone can introduce new programming languages, how do you keep an oversigh There are other risks on using many programming languages.  There must be.  But they are greatly overshadowed by the benefits.  Namely the motivation and efficiency of the developers turning your company strategies into an automated version of reality. -## An all-inclusive playground +### An all-inclusive playground All or nothing fails to innovate.  We should start embracing the new kids on the block.  Languages, or frameworks, with a limited track-record could be of great benefit.  I will give Ballerina a swirl, a language released just a few days ago.  I’ll test run it to appreciate its benefits, and discover its shortcomings.  For a virtually non-existent cost, I will have replaced an existent microservice in a real-world application. Diversity is what makes all ecosystems thrive.  It is time to apply it to your company. -*This document has been adapted from Aad Versteden's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/02/23/all-or-nothing-fails-to-innovate/)* +*This part has been adapted from Aad Versteden's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/02/23/all-or-nothing-fails-to-innovate/)* + +## mu.semte.ch as the ideal playground +![](http://mu.semte.ch/wp-content/uploads/2017/01/juggling_with_languages.png-200x300.png)The [mu.semte.ch](http://mu.semte.ch/) framework is the ideal playground for the developer that wants to try a new language. Using the standards set out by the framework you can, in literally no time, get a (tiny) microservice up and running in your next favourite language. We are ever expanding the range of languages covered by the templates. + +### What are these templates? +Within [mu.semte.ch](http://mu.semte.ch/) a template is everything a microservice needs except for your domain specific logic. Take the javascript template ([https://github.com/mu-semtech/mu-javascript-template](https://github.com/mu-semtech/mu-javascript-template)) for instance: to start a new microservice you first create a directory and a Dockerfile. In that Dockerfile you specify the FROM as the template +```Dockerfile + FROM semtech/mu-javascript-template +``` + +and then you add a app.js file where you enter something like +```js + import { app, query } from 'mu'; + + app.get('/', function( req, res ) { + res.send('Hello mu-javascript-template'); + } ); +``` + +With this you have your first javascript microservice that will respond to the ‘/’ route with a simple hello world string (I know not entirely true, you will also need to configure the dispatcher but for that look at our docs on [mu.semte.ch](http://mu.semte.ch/)). After this you can go wild with javascript and you don’t have to worry about the boring stuff that make the webservice tick, allow you to write/read to the database, etc. + +### Other languages +If we compare this with the [ruby template](https://mu.semte.ch/2017/04/13/building-your-first-microservice-with-sinatra/) then we find that for ruby your FROM statement is slightly different (you guess what changes) and instead of a app.js file you now create a web.rb file and again the simplest form looks almost like the example above. + +Suppose you want to go for python? Well exactly the same. + +Doing this we ensure that you have a similar workflow where you know what to expect with every language that you try through a [mu.semte.ch](http://mu.semte.ch/) template. Allowing you to focus on learning and playing with your newest toy. + +### Just playing? +But wait there is more! You do not have to restrict yourself to just playing. Since the ideas behind a microservice are that they have a very limited set of responsibilities, they can very easily be replaced . If there is a necessity for it you can include your new pet language in a production system with little to no risk at all! This shows one of the biggest strengths of the platform. By standardising on HTTP, JSONAPI and graph databases we ensure freedom to use any tool or language and as such also the tool or language that is the best for the job! + +### Future +The set of templates is, for now, rather limited but check us out regularly because we hope to expand and add many many languages to our portfolio. Of course if you want to implement a template feel free to contact us. We will provide you with a simple set of guidelines that will help you develop that template. + +*This part has been adapted from Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/05/04/mu-semte-ch-as-the-ideal-playground/)* From 2086c0703e9fd4dc8223ba602d874c66a770836d Mon Sep 17 00:00:00 2001 From: Denperidge Date: Tue, 24 Jan 2023 07:34:32 +0100 Subject: [PATCH 06/52] Added reference navigation to root README --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 0bb2da8..29dbd16 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ This repository is used to track issues that apply cross-services in the full semantic.works stack. + +If you want more information behind the design of semantic.works, you can read the following discussions: +- [Why semantic technology?](docs/discussions/why-semantic-tech.md) +- [Reactive programming](docs/discussions/reactive-programming.md) +- Experimentation + - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) + - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) From df1c275353943d4454f7799eef3b662ae5794bf2 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Tue, 24 Jan 2023 07:55:27 +0100 Subject: [PATCH 07/52] Imported Semantic Micro Services, Why Bother? --- README.md | 6 ++-- .../discussions/why-semantic-microservices.md | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 docs/discussions/why-semantic-microservices.md diff --git a/README.md b/README.md index 29dbd16..1878c93 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ This repository is used to track issues that apply cross-services in the full semantic.works stack. If you want more information behind the design of semantic.works, you can read the following discussions: -- [Why semantic technology?](docs/discussions/why-semantic-tech.md) +- **Why semantic...** + - [... technology?](docs/discussions/why-semantic-tech.md) + - [... microservices?](docs/discussions/why-semantic-microservices.md) - [Reactive programming](docs/discussions/reactive-programming.md) -- Experimentation +- **Experimentation** - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) diff --git a/docs/discussions/why-semantic-microservices.md b/docs/discussions/why-semantic-microservices.md new file mode 100644 index 0000000..c83cd46 --- /dev/null +++ b/docs/discussions/why-semantic-microservices.md @@ -0,0 +1,31 @@ + +# Why Semantic Micro Services? (Semantic Micro Services, Why Bother?) +![](http://mu.semte.ch/wp-content/uploads/2017/06/semantic_architect-1024x768.png) + +## What is a semantic microservice? +What I mean (in this post) with a semantic microservice is a small self-sustainable piece of software. That software should only handle one specific logic. And it has to have a semantic model, a model that is expressed in RDF with the usage of (preferably) published controlled vocabularies. + +## Decoupling the dependencies + +Each piece of software essentially operates on a model, an abstract representation of the world, and manipulates that model to fulfil the tasks it has. In traditional software approaches this model, for persistence purposes at least, is described in normalised tables. Most of the time the internal software model is designed to reflect this. + +In any case there is a requirement on the logical level of the software to be aware of that model that can be found in the database. This is a dependency that can be found between all microservices that compose a model. True, this is not an obstacle that cannot be overcome. If you look at Drupal you see that all modules that you install happily work together. This is because they share the same database model. Of course the pieces of logic that are not Drupal-specific also need to be aware of this model. + +But it goes further than that. Traditional microservices are bound together by that shared model. In any case if I have a table describing company personnel then that table will contain all data that is available to the software. Of course there is a practice of splitting of information blocks that are shared between different types of entities (addresses being the typical example). + +With RDF there is no such dependency. Since triples are used to express the model there is no need for a combined model. When it comes to the standard user information in the [mu.semte.ch](http://mu.semte.ch/) stack for instance, you will see that a user has the following properties: a login name, a password, a real name, an email address and a date of joining. But the login microservice doesn’t care about the last 3 “fields”. So it restricts itself to the predicates for login and password. After that it adds a session “field” to that user in the database which in turn is not even being considered by the subscription microservice. + +## Real reusage of microservices +This semantic model also allows us to completely reuse microservices without having to consider the model used. Here the controlled vocabularies help a lot. If I have knowledge described in my application and should I have had the sensibility to describe that knowledge with the SKOS vocabulary then I can take just about any taxonomy/concept scheme microservice and install it in my system. The beauty of these semantic vocabularies is that the disambiguate meaning. In a normalised configuration we would have ended up with tables that would for instance be called concept\_scheme, ConceptScheme, conceptScheme, CONCEPT\_SHEMA, taxonomy, Taxonomy, … not to mention the same in dutch or whatever language. The controlled vocabularies allow us to identify the “class” of a concept scheme without ambiguity. When you consider properties this advantage only gets bigger. + +If this interests you just google “semantic web” or “linked open data”. + +## SPARQL, Triples and Reactive software + +SPARQL is the language that is used to query an RDF store. These store usually express statements in triples. Within the [mu.semte.ch](http://mu.semte.ch/) microservice framework we have developed a component that calculates changes introduced by queries in the database. This component then notifies microservices that have subscribed to these changes. This allows us to take a microservice that was developed for a certain task, plug it in our application and without any wiring have it “react” to what other microservices are doing. + +This also replaces our needs for a message queue. One can of course point out that such a message queue has several advantages such as speed. But it also comes with a hidden disadvantage. Namely every action taken or message send has to be formatted in some way. This is yet again another dependency in your software system. Any microservice that you want include in your system and that needs to process the messages from certain other components will need to be developed with the knowledge of that message model in mind. + +Another dependency removed with semantic technologies… + +*This document has been adapted from Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/06/15/semantic-micro-services-why-bother/)* From 359252f70ad8169049bdbe301c25434f7a208952 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Tue, 24 Jan 2023 08:04:52 +0100 Subject: [PATCH 08/52] Imported Find your way through the stack --- README.md | 6 ++++++ docs/reference/naming-conventions.md | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 docs/reference/naming-conventions.md diff --git a/README.md b/README.md index 1878c93..6cc08af 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ This repository is used to track issues that apply cross-services in the full semantic.works stack. +## Reference +- [Naming conventions](docs/reference/naming-conventions.md) + +## Discussions If you want more information behind the design of semantic.works, you can read the following discussions: - **Why semantic...** - [... technology?](docs/discussions/why-semantic-tech.md) @@ -8,3 +12,5 @@ If you want more information behind the design of semantic.works, you can read t - **Experimentation** - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) + +*This document has been adapted from Erika Pauwels' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/06/22/find-your-way-through-the-stack/)* diff --git a/docs/reference/naming-conventions.md b/docs/reference/naming-conventions.md new file mode 100644 index 0000000..b8844f6 --- /dev/null +++ b/docs/reference/naming-conventions.md @@ -0,0 +1,24 @@ +# Naming conventions (Find your way through the stack) +![](http://mu.semte.ch/wp-content/uploads/2017/06/naming-1024x768.png) + +The mu.semte.ch framework consists of a number of components. A number that is growing steadily and will keep growing in the future. If you’re new to the stack, it’s not always easy to find your way through it. Therefore, we introduced some naming conventions. By adhering to these naming conventions we try to facilitate the hunt for the component you’re looking for. + +Roughly speaking, we distinguish between 5 types of components in the mu.semte.ch framework: + +* Core components +* Services +* Templates +* Ember addons +* Utilities + +**Core components** – as the name reveals – are the core of the mu.semte.ch framework. Without these components, the framework cannot function correctly. Each of the core components is prefixed with ‘mu-‘. Core examples include [mu-identifier](https://github.com/mu-semtech/mu-identifier) and [mu-dispatcher](https://github.com/mu-semtech/mu-dispatcher). + +**Services** are the semantic microservices that compose the backend of an application. Each microservice is postfixed with ‘-service’. A lot of microservices are [already available](https://mu.semte.ch/components/#services), like a login service, a registration service, an export service etc. In case a microservice needs to be configured in a specific programming language the name may also include the name (or an abbreviation) of the programming language name. For example, a service named export-ruby-service requires an export configuration in Ruby while export-js-service requires a configuration in javascript + +**Templates** are the starting points to build a microservice.  Since the programming language of a template is important – you will need to develop the microservice in this language – the name of the language is included in the template name. They are all postfixed with ‘-{language}-template’ like for example [mu-ruby-template](https://github.com/mu-semtech/mu-ruby-template) and [mu-javascript-template](https://github.com/mu-semtech/mu-javascript-template). + +**Ember addons** are the building blocks for the frontend application. Basically, it are just NPM packages you can install in your Ember app. We follow the Ember community naming convention by prefixing the package names with ’ember-‘.  To indicate they are part of the mu.semte.ch stack we also add ‘mu-‘ to the name. E.g. ember-mu-login. + +**Utilities** is the collective term of all tools that facilitate the initiation and development of a mu.semte.ch application. Since they are so diverse, they don’t follow a strict naming convention. The name just tries to cover the functionality. Nonetheless, similar utilities have a similar name. For example, all generators are postfixed with ‘-generator’. + +All the components are published on GitHub where they are also added tagged so you can easily find what you’re looking for. E.g. the core components are tagged with ‘mu-project’, while the services are tagged with ‘mu-service’ etc. All the rules described above are just some simple and straightforward naming conventions, but adhering to them may drastically speed up the search process in the future. From bd7ab3d9d0fe7e31e9e80f80c3fa7af13ae48885 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Tue, 24 Jan 2023 10:28:23 +0100 Subject: [PATCH 09/52] Added reference/documentation --- README.md | 1 + docs/reference/documentation.md | 48 +++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 docs/reference/documentation.md diff --git a/README.md b/README.md index 6cc08af..c6d1b11 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ This repository is used to track issues that apply cross-services in the full se ## Reference - [Naming conventions](docs/reference/naming-conventions.md) +- [Documentation](docs/reference/documentation.md) ## Discussions If you want more information behind the design of semantic.works, you can read the following discussions: diff --git a/docs/reference/documentation.md b/docs/reference/documentation.md new file mode 100644 index 0000000..59cd6c9 --- /dev/null +++ b/docs/reference/documentation.md @@ -0,0 +1,48 @@ +# mu-semtech documentation standards +- Our documentation follows the amazing documentation system used at Divio. [You can view the 30min talk and/or documentation on their website](https://documentation.divio.com/), and/or use the [checklist below](#checklist) to see if the documentation you've written fits. +- The docs... + - ... about semantic microservices/mu-semtech as a whole should be located in this repository ([mu-semtech/project](https://github.com/mu-semtech/project)). + - ... concerning specific microservices should be located inside their respective repositories. + + +## Checklist +This checklist is an attempt at boiling down the divio documentation system on how to write better documentation into something unobtrusive enough for people familiar with it to use as a checklist, but instructive enough for people unfamiliar with it to somewhat do the thing. + +### Tutorials? +For each tutorial: write as if you're *teaching a child how to cook*. + +Checklist: + +- [ ] Holds the readers hand from start to finish +- [ ] Gives a sense of achievement +- [ ] Works for everyone, with minimal explanation +- [ ] Also teaches what ***you*** take for granted + +### How-To guides? +For each how-to: write down how to achieve **1** specific thing as if it were a *cooking recipe*. + +Checklist: + +- [ ] Only 1 practical goal per how-to +- [ ] Minimal explanation +- [ ] Flexible: works for different but similar uses + + +### A reference? +For each reference: write as if you are writing an *encyclopedia* article. + +Checklist: + +- [ ] Structured like the codebase +- [ ] Doesn't explain common tasks ([how-to guides](#how-to-guides)) +- [ ] Doesn't explain basic concepts ([tutorials](#tutorials)) +- [ ] Fully describes the machinery + +### Explanations: discussions/background material? +For each background material: write as if you're writing about the *history and context* of a subject. + +Checklist: + +- [ ] Explains design decisions +- [ ] Considers alternatives +- [ ] Helps the reader make sense of things From ccf85afdcd27518f4a56167b7154e83eb77cb8a5 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Tue, 24 Jan 2023 11:09:51 +0100 Subject: [PATCH 10/52] Imported how-to, fixed incorrect adapted location --- README.md | 3 +- docs/how-to/developing-inside-containers.md | 84 +++++++++++++++++++++ docs/reference/naming-conventions.md | 2 + 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 docs/how-to/developing-inside-containers.md diff --git a/README.md b/README.md index c6d1b11..d0df9d1 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,5 @@ If you want more information behind the design of semantic.works, you can read t - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) -*This document has been adapted from Erika Pauwels' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/06/22/find-your-way-through-the-stack/)* +## How-to +- [Develop with your local IDE and tools inside a Docker container](docs/how-to/developing-inside-containers.md) diff --git a/docs/how-to/developing-inside-containers.md b/docs/how-to/developing-inside-containers.md new file mode 100644 index 0000000..e2623b0 --- /dev/null +++ b/docs/how-to/developing-inside-containers.md @@ -0,0 +1,84 @@ +# Develop with your local IDE and tools inside a Docker container + +## docker-compose +[mu.semte.ch](http://mu.semte.ch/) applications consist, in the back end, of microservices. Those microservices are docker containers that run a lightway server framework and offer logic on certain routes. Those routes are described in the dispatcher and the composition of the microservices is described in the docker-compose.yml file. + +Generally it is undesired that a microservice would talk to another microservice. In exceptional circumstances this is however allowed. We will maybe at some other point publish a blog post describing these cases but for this post I will use the example of a cache microservice. Conceptually this is a simple idea that caches the responses that a microservice returns on certain requests. In this case you would need to know the calls that come in from the front end, through the identifier and the dispatcher and the responses that come from the microservice in question. + +It is clear that mocking this is non-trivial. + +## front end complexity +Within the [mu.semte.ch](http://mu.semte.ch/) applications a lot of complexity is sewn together in the front end. The back end is an API that offers logic on the world (SPARQL endpoint) and the front end calls it. There are many examples of times when it is hard to figure out exactly what is going wrong. You could use a library that abstracts the API you are consuming away but how will you then know exactly which calls it makes? + +You could open your developer tools and copy the network calls but this would still fail to capture the mutations that happen in the identifer and the dispatcher. Furhter it can be really handy to just click and see the calls happening while your break points are conditional and thus only fire when needed. Clickig costs less time than fabricating curls. + +## wrapping 3rd party applications +While it is true that it’s not too hard to wrap applications like SOLR, Elastic Search, Spark, Unified Views, … in a docker. It is far more convenient to see how the tool reacts when you can run it locally as opposed to in a docker. While this is certainly not the largest use case it can be frustrating to figure out what is wrong with such an application when something goes wrong. In this case it also helps if you can develop on localhost. + +## so we develop locally +One could exec in the running docker and dev from there. Most major language offer reasonable dev and debugging tools for the command line. But still this never matches the full blown capabilities of an IDE such as IntelliJ, Visual studio, Atom or \[you favorite here\]. + +![](http://mu.semte.ch/wp-content/uploads/2017/07/mu-proxy-1024x768.png) + +But we also want to have the convenience of having the docker composition, the complete application, there. The wiring can be complex, so can the logic, so mocking this entirely is in most cases unfeasable. + +Therefor we use, for these use cases, a proxy that proxies the calls that would be made to your microservice in the docker-composition (dispatcher by the dispatcher to “http://your-microservice:port/route”) to your localhost. + +## how to +Suppose we have a docker-compose.yml file that somewhere between it’s lines has the following: +```yaml + # ... + - ./config/resources:/config +YOUR_MICROSERVICE: + image: your_microservice:1.0 + ports: + - "3000:3000" + volumes: + - ./config/your-microservice:/config +``` + +Then when running in the complete application your microservice would receive it’s real calls on port 3000 within its container. To have this redirect to your localhost we use just another instance of the dispatcher image, changing the configuration to: +```yaml + # ... + - ./config/resources:/config +YOUR_MICROSERVICE: + image: semtech/mu-dispatcher:1.0.1 + volumes: + - ./config/localhost_dispatcher:/config + ports: + - "3000:80" +``` + +Just as the standard [mu.semte.ch](http://mu.semte.ch/) dispatcher the localhost dispatcher has a `dispatcher.ex` file. Here you would find something along the lines of: +```ex +defmodule Dispatcher do + + use Plug.Router + + def start(_argv) do + port = 80 + IO.puts "Starting Plug with Cowboy on port #{port}" + Plug.Adapters.Cowboy.http __MODULE__, [], port: port + :timer.sleep(:infinity) + end + + plug Plug.Logger + plug :match + plug :dispatch + + match "/*path" do + Proxy.forward conn, path, "http://[YOUR LOCALHOST'S IP]:5678/" + end +end +``` + +As you can see this will forward any incoming call on port 80, which is in the `docker-compose.yml` file mapped to port 3000 ensuring that this dispatcher will serve (as an underboundary) the calls that your microservice would need to support. + +A second observation would be the exposed port on your localhost, that can of course not be 3000 in this setup because we already fire a docker on that port. + +## tada +To make this work you only have to fire up whatever you want to test locally on port 5678 and TADA you can dev on your local machine with your local tools as if you were running everything in a docker and in a docker-compose.yml composition. + +Of course sane thing to do here is to use emacs for development. The terminal version can be installed in a docker and you can dev by exec-ing into it :). + +*This document has been adapted from Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/07/20/develop-with-your-local-ide-and-tools-inside-a-docker-container/)* diff --git a/docs/reference/naming-conventions.md b/docs/reference/naming-conventions.md index b8844f6..d0026fb 100644 --- a/docs/reference/naming-conventions.md +++ b/docs/reference/naming-conventions.md @@ -22,3 +22,5 @@ Roughly speaking, we distinguish between 5 types of components in the mu.semte. **Utilities** is the collective term of all tools that facilitate the initiation and development of a mu.semte.ch application. Since they are so diverse, they don’t follow a strict naming convention. The name just tries to cover the functionality. Nonetheless, similar utilities have a similar name. For example, all generators are postfixed with ‘-generator’. All the components are published on GitHub where they are also added tagged so you can easily find what you’re looking for. E.g. the core components are tagged with ‘mu-project’, while the services are tagged with ‘mu-service’ etc. All the rules described above are just some simple and straightforward naming conventions, but adhering to them may drastically speed up the search process in the future. + +*This document has been adapted from Erika Pauwels' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/06/22/find-your-way-through-the-stack/)* From 99980dba25bf89a2963953f9b5a4bd8d2298095a Mon Sep 17 00:00:00 2001 From: Denperidge Date: Wed, 25 Jan 2023 19:20:11 +0100 Subject: [PATCH 11/52] Added representing-logged-in-users --- README.md | 2 + .../reference/representing-logged-in-users.md | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 docs/reference/representing-logged-in-users.md diff --git a/README.md b/README.md index d0df9d1..3fa130b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ This repository is used to track issues that apply cross-services in the full semantic.works stack. ## Reference +For technical information in semantic.works, you can see the following references: - [Naming conventions](docs/reference/naming-conventions.md) - [Documentation](docs/reference/documentation.md) +- [Represting logged in users](docs/reference/representing-logged-in-users.md) ## Discussions If you want more information behind the design of semantic.works, you can read the following discussions: diff --git a/docs/reference/representing-logged-in-users.md b/docs/reference/representing-logged-in-users.md new file mode 100644 index 0000000..9ee0b31 --- /dev/null +++ b/docs/reference/representing-logged-in-users.md @@ -0,0 +1,53 @@ +# Representing logged in users + +![](https://mu.semte.ch/wp-content/uploads/2017/08/customumize_for_user-1024x768.png) + +How do microservices know who’s logged in?  [mu.semte.ch](https://mu.semte.ch) makes the assumption that microservices can work together automatically if they use the same semantic model.  Microservices know who is logged in by following the correct semantic model.  The semantic model to follow is explained here. + +The difference in life-span of a session and a user account is very different.  The session is ephermal, if a user agent doesn’t show up for too long we may want to clean its session information.  The user’s account is longer lasting.  If you revisit our app five years from now, we want to remember your profile. + +## Account information +The [FOAF](http://xmlns.com/foaf/spec/) ontology makes a difference between a User and a User Account.  This split allows you to have many accounts through which you can log in. + +The user information is commonly represented by an instance of class [foaf:Person](http://xmlns.com/foaf/spec/#term_Person).  This subclass of [foaf:Agent](http://xmlns.com/foaf/spec/#term_Agent) represents people with their naming info.  If you need access to display the name of the person, find it in [foaf:name](http://xmlns.com/foaf/spec/#term_name).  This contains the preferred name for the user, be it a full name, given name, or nickname.  Unless the application domain requires it, this name may not be unique. + +The account information is represented by an object of class [foaf:OnlineAccount](http://xmlns.com/foaf/spec/#term_OnlineAccount).  We attach the username to the account by connection of [foaf:accountName](http://xmlns.com/foaf/spec/#term_accountName).  An example would be the twitter handle, the email address, or the nickname by which you can login into the account.  If your users can login both through twitter and through username/password then a user can have two instances of foaf:OnlineAccount linked to their foaf:Person.  Find the accounts for a user by following the [foaf:account](http://xmlns.com/foaf/spec/#term_account) predicate from the foaf:Person. + +The distinction between foaf:OnlineAccount and foaf:Person is important when adding information to the user.  Most information will be attached to the foaf:Person.  Information may be specific to a single account, or attached to the user in general.  Each account through which the user may login may have its own icon attached to it.  The provider of the account stores the icon and we may want to display it.  This icon would be attached to the foaf:OnlineAccount.  If the user has an icon to be shown when the application refers to the user, then this icon should be attached to the foaf:Person.  The same goes for interests, age group, past orders, etc. + +## Session information +The session URI for each user is generated by the mu-identifier.  It is available in the MU\_SESSION\_ID header.  The session URI points to the foaf:OnlineAccount after the user logged in by use of the predicate musession:account. + +You may want to attach much more information to the session than just the account as explained [here](https://mu.semte.ch/2017/04/27/attaching-data-to-the-users-session/).  You could let users play with your application before they create an account.  Getting them hooked before they have to invest.  You may want to let them fill in their shopping cart, and only let them login or register when they’re ready to place their order.  A reactive microservice can move the information around as necessary. + +## Overview of used ontologies +A few ontologies, types, and predicates have been mentioned here.  We list them in this overview for easy reference. + +***Used prefixes*** +| Prefix | URL | +| --------- | ----------------------------------------------- | +| foaf | http://xmlns.com/foaf/0.1/ | +| mu | http://mu.semte.ch/vocabularies/core/ | +| musession | http://mu.semte.ch/vocabularies/session/account | + +***Used types & predicates*** +| Prefixed name | Description | +| ------------------ | -------------------------------------- | +| foaf:Person | Type representing a user on your site. +| foaf:OnlineAccount | Type representing an account through which a user can login to your site. | +| foaf:name | Predicate attaching a ‘name’ to a foaf:Person. Can be anything ranging from handle, nickname to official name. +| foaf:accountName | Predicate attaching a ‘name’ to a foaf:Account. Use this when showing multiple accounts of a specific user. | +| foaf:account | Predicate connecting the Person to his OnlineAccount. From Person to OnlineAccount. | +| musession:account | Predicate connecting the current session to the OnlineAccount by which the user logged in. From session URI to OnlineAccount. | + +***Used HTTP headers*** +| header | Description | +| ------------- | ------------------------------ | +| MU_SESSION_ID | HTTP header containing the URI of the current user’s session. Might not be used in the database yet. | + +Documentation of foaf can be found at [http://xmlns.com/foaf/spec/](http://xmlns.com/foaf/spec/). + +## Conclusion +The model mu.semte.ch uses for representing users has three levels: the session, the account, and the user.  Information can be attached to either of these three, microservices using this model automatically interact with the logged in user. + +*This document has been adapted from Aad Versteden's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/08/24/representing-logged-in-users/)* From 0c6b603386e9e5801a7b8c164267edd5ce50e7fe Mon Sep 17 00:00:00 2001 From: Denperidge Date: Wed, 25 Jan 2023 19:20:37 +0100 Subject: [PATCH 12/52] Renamed reference to references --- README.md | 6 +++--- docs/{reference => references}/documentation.md | 0 docs/{reference => references}/naming-conventions.md | 0 .../representing-logged-in-users.md | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename docs/{reference => references}/documentation.md (100%) rename docs/{reference => references}/naming-conventions.md (100%) rename docs/{reference => references}/representing-logged-in-users.md (100%) diff --git a/README.md b/README.md index 3fa130b..12f6892 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ This repository is used to track issues that apply cross-services in the full se ## Reference For technical information in semantic.works, you can see the following references: -- [Naming conventions](docs/reference/naming-conventions.md) -- [Documentation](docs/reference/documentation.md) -- [Represting logged in users](docs/reference/representing-logged-in-users.md) +- [Naming conventions](docs/references/naming-conventions.md) +- [Documentation](docs/references/documentation.md) +- [Represting logged in users](docs/references/representing-logged-in-users.md) ## Discussions If you want more information behind the design of semantic.works, you can read the following discussions: diff --git a/docs/reference/documentation.md b/docs/references/documentation.md similarity index 100% rename from docs/reference/documentation.md rename to docs/references/documentation.md diff --git a/docs/reference/naming-conventions.md b/docs/references/naming-conventions.md similarity index 100% rename from docs/reference/naming-conventions.md rename to docs/references/naming-conventions.md diff --git a/docs/reference/representing-logged-in-users.md b/docs/references/representing-logged-in-users.md similarity index 100% rename from docs/reference/representing-logged-in-users.md rename to docs/references/representing-logged-in-users.md From 7bdbc3bac4030ad2a59016c9f5aa647d987a94ac Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 12:38:58 +0100 Subject: [PATCH 13/52] Imported How mu.semte.ch can help you beat the 10% --- README.md | 1 + docs/discussions/smaller-readable-code.md | 28 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 docs/discussions/smaller-readable-code.md diff --git a/README.md b/README.md index 12f6892..4b26d1e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ If you want more information behind the design of semantic.works, you can read t - [... technology?](docs/discussions/why-semantic-tech.md) - [... microservices?](docs/discussions/why-semantic-microservices.md) - [Reactive programming](docs/discussions/reactive-programming.md) +- [Smaller & readable code](docs/discussions/) - **Experimentation** - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) diff --git a/docs/discussions/smaller-readable-code.md b/docs/discussions/smaller-readable-code.md new file mode 100644 index 0000000..e7340db --- /dev/null +++ b/docs/discussions/smaller-readable-code.md @@ -0,0 +1,28 @@ +# Smaller & readable code (How mu.semte.ch can help you beat the 10% odds) +There exists a saying in computer science that developers will end up with spending 90% of their time reading through code and 10% writing code. This reduces our effectiveness. + +For every line we write we will end up reading 10 lines first. So a lot of languages like Clojure/Perl/Ruby use this as an argument to state that writing less code equals having less to read. Hence faster development. But are there more ways we can help? + +![](http://mu.semte.ch/wp-content/uploads/2017/08/emacs1percent.png) + +## Consistent abstractions +[mu.semte.ch](http://mu.semte.ch/) helps you build consistent abstractions. We base ourselves on standards like JSONAPI, SPARQL and HTTP. This means more assumptions can be made and less code needs to be read and (because of standards) the assumptions hold for a long time. + +Functionality should be reusable. Isolating functionality by the boundaries of a microservice makes it reusable and easy to understand. + +## Once upon a time +Think back at the time when you were a junior dev. You were set out to fix your first bug on a real project. You sit down, a hundred files staring at you. Good luck. +Which file to choose? With a good system you may be able to launch a debugger and find a starting point, if you received the time to set up development environment. In a perfect code base you go through a few level of abstractions learning each of them to find the issue at point. Reality often shows that even perfect code is hard to get. + +Can we make it slightly simpler? + +In [mu.semte.ch](http://mu.semte.ch/) there is simple code and well known standards. Spaghetti is not an option. A microservice is typically 100 lines of code. + +You are again a junior dev, set out to fix your fist bug. You open the dispatcher, see the relevant microservice, 100 lines of code, all included. + +## Conclusion +We are not saying that we don’t have bugs, we are not saying that nothing else can be wrong but just about all cases this is the way we work. And yes maybe these 100 lines are important and maybe they take you longer to read. However the chance of you quickly finding the bug greatly increases. + +Nice first day of work! + +*This document has been adapted from Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/08/31/how-mu-semte-ch-can-help-you-beat-the-10-odds/)* From 63c7c5f79182f82aa690156770dd27ced8900f33 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 12:40:40 +0100 Subject: [PATCH 14/52] Fixed link to smaller & readable code --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b26d1e..77caef1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ If you want more information behind the design of semantic.works, you can read t - [... technology?](docs/discussions/why-semantic-tech.md) - [... microservices?](docs/discussions/why-semantic-microservices.md) - [Reactive programming](docs/discussions/reactive-programming.md) -- [Smaller & readable code](docs/discussions/) +- [Smaller & readable code](docs/discussions/smaller-readable-code.md) - **Experimentation** - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) From f304005cc521317d1c46e2ac91535b9a66fcb8d8 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 14:23:30 +0100 Subject: [PATCH 15/52] Writeup nav & imported mu.semte.ch at DockerCon EU --- README.md | 6 ++++++ writeups/dockercon-eu-2017.md | 31 +++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 writeups/dockercon-eu-2017.md diff --git a/README.md b/README.md index 77caef1..16b24ab 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,9 @@ If you want more information behind the design of semantic.works, you can read t ## How-to - [Develop with your local IDE and tools inside a Docker container](docs/how-to/developing-inside-containers.md) + + +## Writeups +Our retrospectives on... +- [OSLO²](writeups/oslo2.md) +- [Dockercon EU 2017](writeups/dockercon-eu-2017.md) diff --git a/writeups/dockercon-eu-2017.md b/writeups/dockercon-eu-2017.md new file mode 100644 index 0000000..bc30f80 --- /dev/null +++ b/writeups/dockercon-eu-2017.md @@ -0,0 +1,31 @@ +# mu.semte.ch at DockerCon EU +The mu.semte.ch stack relies on Docker as it perfectly suites a microservice architecture. We don’t only use it for deployment, but also for development. By using Docker we try to make our workflow as productive as possible. Since the Docker community is moving very fast, we attended DockerCon EU in Copenhagen last week to keep up to date with the latest Docker news. + +![](http://mu.semte.ch/wp-content/uploads/2017/10/dockercon-eu.png) + +The Docker team announced two major topics in the keynotes: + +- [seamless integration of Kubernetes into the Docker platform](https://www.docker.com/kubernetes) (alongside Swarm) +- [expanded partnership for the Modernize Traditional Applications (MTA) program](https://goto.docker.com/rs/929-FJL-178/images/SB_MTA_04.14.2017.pdf)  + +The video recordings of the keynotes are freely available on [Keynote Day 1](https://play.vidyard.com/h5fj14BB2Gkai1WHcbAyTv) and [Keynote Day 2](https://play.vidyard.com/wztT1ekFnTjDYLcYfJWALX). + +Traditonal applications are typically developed as monoliths. With the MTA program Docker tries to support companies to upgrade their traditionally deployed apps to a more modern container infrastructure like Docker. Although the mu.semte.ch architecture builds on the concept of microservices, Docker’s MTA program may teach us useful lessons on the migration from monoliths to microservices. After all, projects don’t always start from scratch but often originate from an existing (monolith) solution. + +Next to the keynote sessions there were also a lot of breakout sessions on a broad range of topics. All slides will soon become available on [Docker’s slideshare](https://www.slideshare.net/Docker). We will just highlight a few of the topics covered: + + +- Abby Fuller from AWS explained how to create effective Docker images covering topics like the recently introduced [multistage builds](https://docs.docker.com/engine/userguide/eng-image/multistage-build/). Some of the mu.semte.ch templates and services will benefit from these tips to reduce the size of their Docker image. + +- Adrian Mouat, one of the Docker captains, gave [a talk with some tips and tricks](https://www.slideshare.net/Docker/tips-and-tricks-of-the-docker-captains). Nothing new, no mind blowing stuff, but just some useful practical tips to make your day-to-day Docker usage more efficient. + +- Dan Finneran, an ex-Docker captain that recently became a Docker employee, prestented [an overview of the Docker networking](https://www.slideshare.net/Docker/practical-design-patterns-in-docker-networking). This included the Swarm overlay networks and some practical design patterns the mu.semte.ch architecture might benefit from when deploying on multiple machines. + +- Bret Fisher, another Docker captain, gave a _playful_ talk on [the deployment to production with Docker](https://d.pr/f/d7diCc/1AEjBc2m). He warned for bad practices like turning servers from cattles into pets and making Dockerfiles environment specific. For sure some lessons we should take into account when deploying a mu.semte.ch app. + +- Riccardo Tommasini, a PhD Student from Milano, gave a short presentation in the Community Theatre on his ongoing research to empower Docker with Linked Data principles. One of his research results, a Docker ontology to represent Dockerfiles in RDF, will be [presented next week at ISWC 2017](https://iswc2017.semanticweb.org/paper-528/). + + +These are just a few highlights of all the topics covered at the conference. The video recordings of all presentations will soon become available on the Docker website. A lot of interesting material to learn from within the context of mu.semte.ch. + +*This writeup has been adapted from Erika Pauwels' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/10/19/mu-semte-ch-at-dockercon-eu/)* From 0058f86954e16c2660c124613fa7b26dc7e5276c Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 16:07:53 +0100 Subject: [PATCH 16/52] Imported Publishing ... Docker multi-stage builds --- README.md | 5 ++- docs/discussions/docker-multi-stage-builds.md | 43 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 docs/discussions/docker-multi-stage-builds.md diff --git a/README.md b/README.md index 16b24ab..3f0e356 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,12 @@ If you want more information behind the design of semantic.works, you can read t - **Why semantic...** - [... technology?](docs/discussions/why-semantic-tech.md) - [... microservices?](docs/discussions/why-semantic-microservices.md) -- [Reactive programming](docs/discussions/reactive-programming.md) -- [Smaller & readable code](docs/discussions/smaller-readable-code.md) - **Experimentation** - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) +- [Reactive programming](docs/discussions/reactive-programming.md) +- [Smaller & readable code](docs/discussions/smaller-readable-code.md) +- [Docker multi-stage builds benefits](docs/discussions/docker-multi-stage-builds.md) ## How-to - [Develop with your local IDE and tools inside a Docker container](docs/how-to/developing-inside-containers.md) diff --git a/docs/discussions/docker-multi-stage-builds.md b/docs/discussions/docker-multi-stage-builds.md new file mode 100644 index 0000000..c2f2e51 --- /dev/null +++ b/docs/discussions/docker-multi-stage-builds.md @@ -0,0 +1,43 @@ +# Docker multi-stage builds benefits (Publishing an EmberJS app using Docker multi-stage builds) +![](http://mu.semte.ch/wp-content/uploads/2017/05/mu_semte_ch_of_oz-1024x683.png) + +In [the ember-proxy-service repo](https://github.com/mu-semtech/ember-proxy-service#tutorial-hosting-an-emberjs-app-with-a-backend-api) we explained how to host an EmberJS application in nginx with a backend API.  First, we had to build the EmberJS app through ember-cli’s build command: +```bash +ember build -prod +``` + +Next, we copied the resulting `dist/` folder into the nginx Docker image before building it. This process is cumbersome since it requires two manual steps to publish an EmberJS Docker image. + +It would be more clean if we could execute the ember build step during the Docker image build. However, this requires that (a) the ember build command is available and (b) all our source files and dependencies (e.g. the node_modules folder) are copied inside the Docker container. As a consequence the resulting image would be much larger since it contains a lot more than just the built javascript assets from the dist/ folder. + +To tackle this problem Docker introduced the concept of [multi-stage builds in Docker 17.05.](https://docs.docker.com/engine/userguide/eng-image/multistage-build/)  Multi-stage builds allow to use multiple FROM statements in your Dockerfile. Each FROM statement begins a new stage of the build starting from the specified base image. The base images may differ per FROM statement. Artifacts can be copied from one stage to another. Everything else will be left behind when moving to a next stage. Therefore the resulting image will be much smaller. It only contains the image built in the final stage and the artifacts copied from previous stages. Have a look at the [Docker documentation](https://docs.docker.com/engine/userguide/eng-image/multistage-build/#before-multi-stage-builds) for a more in-depth explanation. + +Our case would benefit from a 2-stage build: + +1. Building the EmberJS assets +2. Building the nginx image to host the EmberJS app + +This would result in the following Dockerfile: +```Dockerfile +# Stage 1 to build the EmberJS assets +FROM madnificent/ember:2.15.0 as builder + +MAINTAINER Erika Pauwels <[\[email protected\]](/cdn-cgi/l/email-protection)\> + +WORKDIR /app +COPY package.json . # postpone cache invalidation +RUN npm install +COPY . . +RUN ember build -prod +# End stage 1 + +# Stage 2 to build the nginx with our EmberJS app +FROM semtech/ember-proxy-service:1.1.0 + +COPY --from=builder /app/dist /app +# End stage 2 +``` + +Docker multi-stage builds are a simple, yet powerful concept to fully automate application build processes while keeping the resulting Docker image small. In our case we were able to reduce the image size from 1.24GB to 185MB. Note however that multi-stage builds don’t work on Docker Hub, which is deprecated in favor of Docker Cloud. + +*This document has been adapted from Erika Pauwels' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/11/02/publishing-an-emberjs-app-using-docker-multi-stage-builds/)* From 8411177bb2ed566236ccc416a7a2842865b4619c Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 16:16:20 +0100 Subject: [PATCH 17/52] Imported Hello MacOS --- README.md | 1 + docs/discussions/supporting-mac-os.md | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 docs/discussions/supporting-mac-os.md diff --git a/README.md b/README.md index 3f0e356..d7ac603 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ If you want more information behind the design of semantic.works, you can read t - [Reactive programming](docs/discussions/reactive-programming.md) - [Smaller & readable code](docs/discussions/smaller-readable-code.md) - [Docker multi-stage builds benefits](docs/discussions/docker-multi-stage-builds.md) +- [Supporting MacOS](docs/discussions/supporting-mac-os.md) ## How-to - [Develop with your local IDE and tools inside a Docker container](docs/how-to/developing-inside-containers.md) diff --git a/docs/discussions/supporting-mac-os.md b/docs/discussions/supporting-mac-os.md new file mode 100644 index 0000000..d61b1bb --- /dev/null +++ b/docs/discussions/supporting-mac-os.md @@ -0,0 +1,25 @@ +# Supporting MacOS (Hello MacOS) +![](http://mu.semte.ch/wp-content/uploads/2017/11/Mac-semtech-1024x768.png) + +Many developers use MacOS on their daily basis.  With our Docker background, we’ve mainly focused on Linux support.  That is about to change, MacOS is becoming a first class citizen. + +## What does this entail? +Base scripts will receive Docker support, documentation will be verified to run both on Linux as well as on MacOS.  We will supply installation instructions for MacOS. + +For most supporting projects we start, we base ourselves on Linux.  The ember docker is a good example of that.  Although Docker abstracts a lot, there are differences.  The scripts for this Docker have received updates already, ensuring they run correctly on MacOS.  By trying out solutions both on Linux as well as on MacOS, we ensure a smooth operating experience. + +We are looking into ways of supplying code for MacOS so the installation is simplified.  For the backend, installation is trivial with Docker for Mac.  For the frontend, we’re considering to support homebrew installation of edi. + +## What differences are considered? +Bigger differences for us come in the form of filesystem performance, supplied shell scripts, and user namespaces. + +Filesystem performance for mounted volumes is an issue for MacOS.  Both starting of containers, as well as syncing content between the container and the host seems to be an order of magnitude slower.  We are updating scripts to ensure the system works as expected.  Performance improvements are currently only noticeable on MacOS. + +With Linux we target a modern kernel and a modern Bash environment.  The shell scripts Linux ships with are slightly different than those MacOS has in store.  This means some scripts need to be adapted. Some will become cleaner, some will become more complex. + +User Namespaces ensure we can generate files from Docker, with your access rights.  This alleviates the issue where you need to chown generated files.  Docker for Mac doesn’t have that issue.  It does not run as root, hence the containers generate files under your own username. + +## In conclusion +If your organisation is a cross-breed between Linux and MacOS, it will become easier to get everyone on board with mu.semte.ch.  We’ll keep you posted as we hum along. + +*This document has been adapted from Aad Versteden's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/11/09/hello-macos/)* From a09e683048c049b67edf4d964792be3ec244c892 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 16:47:33 +0100 Subject: [PATCH 18/52] Imported mu.semte.ch at DeveloperWeek --- writeups/developerweek-2018.md | 56 ++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 writeups/developerweek-2018.md diff --git a/writeups/developerweek-2018.md b/writeups/developerweek-2018.md new file mode 100644 index 0000000..ae86886 --- /dev/null +++ b/writeups/developerweek-2018.md @@ -0,0 +1,56 @@ +# mu.semte.ch at DeveloperWeek +![](http://mu.semte.ch/wp-content/uploads/2018/03/developer-week-1024x576.jpg) + +Last February I was lucky enough to be able to give a presentation on the grand ballroom stage at one of the bigger developer conferences in the bay area: [DeveloperWeek](http://www.developerweek.com/). + +I gave a presentation on developing reusable microservices through 10 principles. Principles which we have explored over the years on the mu.semtech blog. What is a reusable microservice? Without giving a precise definition here are some properties to which such a microservice should adhere: + +- a microservice  should be swappable for another microservice that offers the same endpoints (the typical example is an update of the same microservice) +- a microservice can be considered stateless +- a microservice communicates over HTTP + +This article gives an overview and a small description of the principles and why it helps to develop microservices that are resuable. + +## 1 Single source of truth +From the microservice’s perspective there is only one single endpoint which holds the complete truth about the world. This source can be distributed using this functionality on a database or software functionality layer such as the [SANSA stack](http://sansa-stack.net/). But it can also be composed, think for instance of [Ontario](https://www.ontario.ca/search/data-catalogue). + +## 2 Set of actions on data +Each microservice contains only a little consistent piece of functionality on data. Now that is super vague, right? What is consistent? Well, I don’t mean consistent to the other actions offered by this microservice, but rather consistent to its data model. + +Take for instance login and registration services. Both of them are user related so you might think it is a good idea to offer both functionalities in the same microservice. However this is far from optimal since both functionalities require vastly different data models. The login service will need a data model that manages username, password, date of birth, real name, … while the registration service will be interested in the session, username and password. Even though at a first glance this is a single set of functionalities, namely user management, it is not the case from a data perspective. + +## 3 No horizontal communication +Within the application block no microservice talks directly to another. Take the setup shown below for example. You will see that #white# talks to #dark-blue# but they only talk to each other and in this configuration. But from the perspective of the dispatcher and from that of the database they are a single microservice. This is one of the most important principles. Without this we would start introducing hidden dependencies all over the application. + +![](http://mu.semte.ch/wp-content/uploads/2018/03/developer-week-2-1024x603.png) + +Take for instance if #pink# would call an endpoint on #dark-blue# every time a certain hook gets executed. Or to make it more concrete, #pink# creates an invoice and #dark-blue# sends that invoice to the customer. That is innocent enough but if we remove the #dark-blue# microservice some other service will need to pick the ‘send-invoice’ action up. By allowing these horizontal hooks we create a hidden dependency, a contract between the microservice and the maintainer of the application. If you install me in your application then I expect you to install something to handle actions X, Y and Z. + +The second unwanted side effect of this is that #pink#, even though it only creates the invoice, can only be used if you want to create and mail an invoice. But we still want to handle business logic, if not through hooks and messages then how do we go about implementing that? + +## 4 Business logic through UX +We came up with two solutions to the business logic problem. The first one is to solve it through the UX. Note that this should only be done in cases where it makes sense. We don’t want to just shift the complexity of the backend to the frontend, where it is almost always worse! To illustrate when this is OK we use the example of search. If I have a search microservice and a resource microservice then I feed the search microservice a string and it will give me back an ID. I then use this ID to obtain an object from the resource microservice. This a clean decoupling of both functionalities. The search needs not to know how an object is composed and the resource has no idea about the indexing scheme used. Both can operate, be changed and installed independently. + +## 5 Business logic through reactive programming +To go back to the create invoice and mail example, that is clearly a case of complexity that should not be handled in the frontend. To handle this we have come up with what we call ‘reactive programming’. I know it is an overloaded term but we don’t intend that to be what you get if you google the term. Rather it is a framework functionality that allows a microservice to be notified of the pure data changes (think triples here) that are about to be introduced to the database. For more information have a look at the [reactive programming discussion](../docs/discussions/reactive-programming.md). + +## 6 Configuration +Code that gets used for more than one use case and from more than one angle tends to be robuster and more bug free than code that doesn’t get the same usage.  Furthermore, because it has already been used in a variety of ways chances are that the way you want to put it to use may already be largely supported. Within mu.semte.ch the champion when it comes to reuse is [mu-cl-resources](https://github.com/mu-semtech/mu-cl-resources). Which is our default resource offering microservice. It supports a full fledge JSONAPI compliant service on top of a SPARQL backend. + +## 7 Controlled vocabularies +Controlled vocabularies serve not only to facilitate standardization – which they do very well – but because concepts in vocabularies are represented using URIs they have the ability to be materialized so that they also contain, within themselves, unambiguous documentation as to their intent. + +My favorite example of the incredible power of these controlled vocabularies is the work that Aad and I did recently on [an Ember component that supports Oauth2](https://github.com/mu-semtech/ember-oauth-github-generator). We have designed a backend microservice that handles the storing of the Oauth2 credentials in the database. And it all just magically worked together with a component that Erika had written well over 3 years ago! Out of the box, without us going about checking her implementation. + +## 8 Push updates +We want to provide a standardized way to provide the user with push updates. These push updates will allow us to create applications that are from the user’s perspective incredibly responsive. Our standardization builds on either long pulling or web sockets and Ember Data as a frontend datastore. + +## 9 Reasoning +With the advent of powerful database technologies such as the [SANSA stack](http://sansa-stack.net/) comes the ability to apply reasoning to large datasets and to do it in a time that allows application to behave normally. This reasoning can be used for classic purposes such as implicit knowledge definitions and data quality control but it can also help us reuse more services. + +## 10 Access control +The last part to touch upon is a database that has access control built-in. In this way it allows us to separate this important part of application design from both the business logic and from the data it operates on. The separation from business logic means that we can now build microservices without any access control in mind and still deploy them with access control present. Doing so we achieve, yes, even more reusability. Separation from data empowers us to change the access control rules easier and it allows us more freedom when it comes to defining those rules. + +The complete presentation as given at DeveloperWeek can be found [here](https://docs.google.com/presentation/d/1MJWLmAWXi8pFZqUWS2Tp4bM_bTA-C03iH3p50Nh6MZ4/edit#slide=id.g34f8d1cab0_2_50). + +*This document has been adapted from Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2018/04/19/mu-semte-ch-at-developerweek/)* From b86dad8346b563d905790a4fea305e3269f13470 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 17:00:14 +0100 Subject: [PATCH 19/52] Imported On sharing authorization --- README.md | 1 + docs/discussions/sharing-authorization.md | 84 +++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 docs/discussions/sharing-authorization.md diff --git a/README.md b/README.md index d7ac603..995ee7a 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ If you want more information behind the design of semantic.works, you can read t - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) - [Reactive programming](docs/discussions/reactive-programming.md) +- [Sharing authorization](docs/discussions/sharing-authorization.md) - [Smaller & readable code](docs/discussions/smaller-readable-code.md) - [Docker multi-stage builds benefits](docs/discussions/docker-multi-stage-builds.md) - [Supporting MacOS](docs/discussions/supporting-mac-os.md) diff --git a/docs/discussions/sharing-authorization.md b/docs/discussions/sharing-authorization.md new file mode 100644 index 0000000..155a20d --- /dev/null +++ b/docs/discussions/sharing-authorization.md @@ -0,0 +1,84 @@ +# On sharing authorization +Authorization is a cross-cutting concern\*.  By carving out the pieces each component needs to know, we minimize dependencies, maximize code-reuse, and make the system easy to understand.  In this article, we’ll describe the inner and outer workings of the accessibility model. + +## Who needs to know? +Various entities need to have different information from the authorization system.  The database needs all knowledge, caching systems need a well-structured system for knowing whether a cached result can be used or not. + +The database needs to have full access to the authorization system.  It needs to know what information can be read, and what information can be written.  As all information about the users should be stored in the database, the database itself should be able to define the access rights for a particular user.  Requirement: the database needs to receive the user’s session.  To some effect, the database can be considered the authority on user access information.  The database can derive the access rights and interpret them based on the session identifier. + +Common microservices shouldn’t be bothered with access rights.  A simple microservice would execute operations (read or write) on the database in the name of the user.  If the microservice requests information which it is not allowed to see, the database will reject the operation.  Otherwise, the request passes through, based on the information this particular user is allowed to see.  Responsibility is thereby fully handed over to the database and the microservice needs no information about the access rights. + +Microservices are allowed to cache results in separate databases, as long as they abide the semantic model.  With authorization handled by the database, this model now also consists of the authorization information.  The construction of a cache could take many shapes, and optimizations may be dependent on the specific authorization scheme.  It is our goal to provide a usable interface for common caching scenario’s.  A cache should be able to automatically detect whether it applies to the access rights of a particular user, and whether or not it can be used for other users. + +In an ideal world, these caches can work automatically.  Any system may have unsupported cases.  We aim to support them with escape hatches as they appear. + +## Defining access right tokens +All operations which occur in the stack are based on SPARQL queries.  Hence, the access right tokens should apply to SPARQL queries.  We consider two interesting sets: one set contains all access rights of the current user, the other set contains the access rights used to answer the query. + +### Scoping access right tokens + +In order to intelligently apply and use access right tokens we need to be able to compare them.  The tokens are JSON objects with two keys: *name* and *variables*. + +Examples are: +```js +{ name: "user_basket", variables: ["42"] } +{ name: "public", variables: [] } +{ name: "suborganization_tickets", variables: ["rocket_engineers","nozzle_team"] } +``` + +In each of these examples, the *name* specifies a certain grouping, and the variables present a configuration of that grouping.  The logic constraints of these two properties will be described later. + +### Access rights and executing queries +The SPARQL endpoint has all information available to execute a query.  The endpoint will emit both the access rights of the current user, as well as the used access rights to answer the query.  Let’s explain that in a bit more detail. + +The access rights of the current user is an array of access tokens.  An example could be: +```js +[{ name: "public", variables: [] }, { name: "user_basket", variables: ["42"] }] +``` + +This user has access to all public information as well as to their own shopping basket.  These access rights will not change during normal operation.  Changes can only occur by logging in/out or explicitly receiving new access rights.  Hence the access rights can be cached. + +Now assume the user executes a query for the names of all publicly available products in the webshop.  In such a case, the “public” access token is used, but no content for the “user_basket” token would be used.  In fact, in a realistic scenario, no user_basket would ever contain a product name.  The access right system could share that the user had these two access tokens, but that only the *public* access token was used in this request. + +We specify the data structures of a token by use of the *name* property.  Each token with the same *name*, regardless of its *variables* may contain the same data structures.  The variables define scopings of the actual data.  the user_basket with variable 42 may contain different information than the user_basket with variable 68. + +The access right system shares all groups to which a user had access in the MU_AUTH_ALLOWED_GROUPS response header.  The groups which could have been used to answer the query is returned in the MU_AUTH_USED_GROUPS.  The latter contains all groups which, based on the structure of their data, could have helped to provide an answer. + +## Resulting properties +The authorization system has some interesting implications for practical implementation.  The most prominent ones are described in this section. + +Tokens are communicated as JSON properties in the MU_AUTH_ALLOWED_GROUPS and MU_AUTH_USED_GROUPS headers.  These base technologies are already part of the mu.semte.ch microservice requirements. + +A large set of microservices operate in the name of the current user, and can be implemented transparently.  These microservices can be upgraded solely by upgrading their microservice template.  Resulting in virtually no impact on existing services. + +The tokens for a particular session are near-constant.  The only very common case for changing tokens is logging in/out.  This means that for almost all requests the user makes, we can use a cached set of access tokens.  These access tokens can be stored in the user’s session. + +When executing a SPARQL query, the set of mu_auth_allowed_groups will always be a superset of mu_auth_used_groups. If a group with name foo was in mu_auth_allowed_groups and not in mu_auth_used_groups, then this will be the case for all further executions of this query, regardless of the variables applied to the tokens. Because of this we can... +1. Cache the result of a product listing query for a user which has tokens: + ```js + [{ name: “public”, variables: [] }, { name: “user_basket”, variables: [“42”] }] + ``` +2. Discover only... + ```js + [{ name: “public”, variables: [] }] + ``` + ...was used to answer the query by checking the mu_auth_used_groups +3. And use the response for a user with tokens ... + ```js + [{ name: “public”, variables: [] }] + ``` + ... or ... + ```js + [{ name: “public”, variables: [] }, { name: “user_basket”, variables: [“68”] }] + ``` + + +Results of executing multiple SPARQL queries can be simply combined. Say a user has `tokens [a,b,c,d]`. Assume a first SPARQL query has `used_groups [a,b]` and a second SPARQL query has `used_groups [a,c]`. The resulting required access tokens for the full response would become `[a,b,c]`. As such, it is easy for systems to combine the access rights for SPARQL queries. This property allows for simple `access-rights-aware` caching. + +## Conclusion +The authorization system can work by use of access tokens.  Most microservices can be upgraded merely by bumping their base microservice template.  The access rights tokens are JSON objects which allows us to inspect them.  The system is near-transparent for the end-user. + + +(*) The vast majority of authorization could be handled by the database. Based on who is logged in, we can define what information they are allowed to see or manipulate. Knowing the session, we could derive all information about access rights at the moment the user logs in. However, there’s a catch. Microservices are allowed to have local caches. As these caches can be stored locally, responses may be generated without requests to the database. In order to keep this path open, we need to be able to share access rights to these systems. + +*This document has been adapted from Aad Versteden's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2019/01/02/on-sharing-authorization/)* From 815451c73dc9527f7e7cba80e4a9b7bdf8767a68 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 17:04:08 +0100 Subject: [PATCH 20/52] Imported On microservice reuse and authorization --- .../microservice-reuse-and-authorization.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 docs/discussions/microservice-reuse-and-authorization.md diff --git a/docs/discussions/microservice-reuse-and-authorization.md b/docs/discussions/microservice-reuse-and-authorization.md new file mode 100644 index 0000000..742c65b --- /dev/null +++ b/docs/discussions/microservice-reuse-and-authorization.md @@ -0,0 +1,55 @@ +# On microservice reuse and authorization +Microservices make it easy to reuse logic across applications.  You can easily share the logic between applications by sharing the full microservice.  But there’s a catch, you must share the full microservice.  Hence, you must be able to use that exact logic.  *When a microservice embeds authorization logic, it can only be reused in domains with the same authorization logic.* + +## Examples of authorization rules +Authorization rules boil down to the definition of the boundaries of information sharing.  In order to define *who* can see *what* information, the *who*<> and the *what* need to be defined. + +As described in earlier blog-posts, the *who* can be connected to the session of the user.  Based on the information in the session, and a SPARQL query, we should be able to formalize the access groups of a user.  Practical examples of *who* are + +- an individual user, +- a user belonging to an organization, +- a user which has a specific role in an organization. + +The *what* is a less explored domain in the mu.semte.ch stack.  Our goal with *what* is to find a definition which is sufficiently easy to use, but which covers sufficient use-cases.  Cases we’ve discovered are: + +- access to instances of an entity, +- access to properties of an entity, +- access to aggregated results. + +These two topics can be combined.  The *who* and the *what* can be combined.  The visitor of a webshop could be allowed to see only *his own* (who) *shopping baskets* (what).  Without logging into the webshop, *any user* (who) might be allowed to see all *products* (what). + +Aggregated results are a different beast.  A user which hasn’t logged in might be allowed to see the total amount of orders placed on the webshop, to indicate the trustworthiness.  This is an aggregated result.  A user may also be able to see how many orders were placed within his region, given that sufficient orders were placed in the region.  The constraints we could place in this domain seem very broad, and we feel we’ve only scratched the surface.  We’re leaving this to be described and tackled as we gain more experience. + +Access rights could be defined in terms of *who* can access *what*.  The *who* boils down to users, groups, and individuals. The *what* which we aim to tackle are instances of a type, and properties of such instances.  The combination of *who* and *what* define what users will eventually be able to access. + +## The boundaries of a microservice +The mu.semte.ch stack makes it super easy to build and share microservices.  With the semantic model, the intended data is clear, with Docker it’s easy to share and launch the services.  We consider these challenges intrinsic to the offered services.  You need to know what you are talking about, in order to offer operations on that data.  For most services, authorization is not an intrinsic challenge.  The same service could be offered in many authorization setups. + +We have proven that we’re able to share a great set of microservices when the authorization domain is constant.  Application clusters built for a single customer, or solutions which work on open data.  Both of these cases hide the complexity of authorization, but in order to reuse the services with other authorization domains, a solution is necessary once again. + +## Moving around authorization +Authorization is a problem which we should tackle transparently to the microservice . When we take a peek at the examples of authorization rules, we notice that many of them work on the data level.  As such, if we could handle authorization in the triplestore directly, we could unlock the reuse of many more microservices. + +We have run experiments and have shown this could be achieved with a meta-SPARQL-endpoint.  A SPARQL endpoint which rewrites the queries of each microservice so they obey the access rights of an individual user.  Although there are quite a few side-remarks, such an implementation makes it plausible to reuse services which weren’t reusable earlier.  This is a complex challenge which we will need to face head-on. + +With the right primitives, we can transparently rewrite the queries a microservice executes so it only operates on information a user has access to. + +## Thoughts on information sharing and access control +Initial discussions revealed various ways in which information can be shared between multiple entities.  Like sharing a book, or filling in a form together. + +In database terms, the most obvious sharing is bidirectional sharing.  Two people work on the same dataset, as if it were a shared spreadsheet.  All changes one person makes are visible to the other. + +Another easy to understand way of sharing is unidirectional.  You rent a book from the library.  You can read the contents, but you can’t push any changes back to the author. + +If you buy the book, you’re allowed to make annotations.  You can highlight contents, add side-remarks, rip whole chapters out of the book and rewrite them in your own phrasing.  You received the information, manipulated it, but do not share it back. + +There’s also the notion of live versus offline collection of information.  In a live collection of information, we’re seeing the updates pass on immediately.  Whereas with bulk updates, we’d download some new contents and integrate them in our memory. + +Secondly, we discover the act of reading versus writing.  You may be allowed to see certain contents, whilst not being able to write them.  You may be able to write certain contents, but not be allowed to read them (you could send log-files to an admin but never be allowed to read them again).  Or you could do both reading and writing. + +In order to keep the approach conceivable, we’re working towards authorization with the three last mentioned forms of reading and writing.  We will consider bidirectional sharing only, and will require push updates to be solvable through escape hatches of the authorization system. + +## Conclusion +Authorization is not an intrinsic complexity of microservices.  Extracting authorization rules allows sharing microservices across more applications.  Many authorization rules can transparently be handled by rewriting the SPARQL queries. + +*This document has been adapted from Aad Versteden's mu.semte.ch article. You can view it [here](https://mu.semte.ch/2019/04/02/on-microservice-reuse-and-authorization/)* From 36cf77842ecd0c3b4ab8a4ed3bae29a9152b4b89 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 17:18:42 +0100 Subject: [PATCH 21/52] Added archive README, archived Get to know mu-cl- --- archive/README.md | 5 +++++ archive/get-to-know-mu-cl-resources.md | 12 ++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 archive/README.md create mode 100644 archive/get-to-know-mu-cl-resources.md diff --git a/archive/README.md b/archive/README.md new file mode 100644 index 0000000..cfeba11 --- /dev/null +++ b/archive/README.md @@ -0,0 +1,5 @@ +# mu.semte.ch archive + +The items in this folder have been archived for any (but not limited to) the following reasons: +- They were info about the mu.semte.ch/ blog +- The services they describe have been deprecated diff --git a/archive/get-to-know-mu-cl-resources.md b/archive/get-to-know-mu-cl-resources.md new file mode 100644 index 0000000..4597265 --- /dev/null +++ b/archive/get-to-know-mu-cl-resources.md @@ -0,0 +1,12 @@ +**Editors note** +The following has been archived due to it being a blog post that was meant as a status update & linking to different blog posts. +The blog posts that the last two links refer to have been merged & imported into mu-projects' documentation. You can view this [here](https://github.com/mu-semtech/mu-project#creating-a-json-api) + +*This document has been adapted from Erika Pauwels' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2018/03/22/get-to-know-mu-cl-resources/)* + +--- + +# Get to know mu-cl-resources +Long awaited, but it’s finally there: [a README for the mu-cl-resources service](https://github.com/mu-semtech/mu-cl-resources). Get to know all features (there are a lot!) of our microservice producing a [JSONAPI](http://jsonapi.org/) compliant API for your resources based on a simple configuration describing the domain. Find things such as [how to define your domain](https://github.com/mu-semtech/mu-cl-resources#configurationdomainlisp), [how to filter](https://github.com/mu-semtech/mu-cl-resources#basic-filtering), [how to paginate](https://github.com/mu-semtech/mu-cl-resources#pagination) and a lot more. + +You can also have a look at our previous blog posts [Generating a JSONAPI compliant API for your resources, part 1](https://mu.semte.ch/2017/07/27/generating-a-jsonapi-compliant-api-for-your-resources/) and [part 2](https://mu.semte.ch/2017/08/17/generating-a-jsonapi-compliant-api-for-your-resources-part-2/) to get started. From bc2f716d0c340beda6a703fe6dee6ae71a0da9c3 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 17:24:07 +0100 Subject: [PATCH 22/52] Archived The delta service and its benefits --- archive/the-delta-service-and-its-benefits.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 archive/the-delta-service-and-its-benefits.md diff --git a/archive/the-delta-service-and-its-benefits.md b/archive/the-delta-service-and-its-benefits.md new file mode 100644 index 0000000..046ad28 --- /dev/null +++ b/archive/the-delta-service-and-its-benefits.md @@ -0,0 +1,48 @@ +**Editors note** +The following has been archived due to delta-service being superseded by delta-notifier. More info on the deprecation can be found in the [delta-service repo](https://github.com/mu-semtech/archived-mu-delta-service) + +*This document has been adapted from Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/05/11/the-delta-service-and-its-benefits/)* + +--- + +# The delta service and its benefits +The delta-service has already been mentioned and used in [previous blog posts on reactive programming](https://mu.semte.ch/tag/delta-service/) to make microservices hook into changes introduced by other microservices. In this article we will elaborate in more depth on this service. What is the delta-service and what are the benefits of using it? + +## What is the delta-service? +![](http://mu.semte.ch/wp-content/uploads/2017/05/wild_e_delta-300x200.png)The delta-service is a microservice that offers a SPARQL endpoint. It accepts SPARQL queries, analyses them, calculates the differences a query introduces into the graph store and notifies interested parties about these differences. + +The reports that will be sent to interested parties are formed with triples as the basic building blocks as triples are the resources that those SPARQL stores (mentally) have in common. + +Conceptually such a report would looks somewhat like this: + + + + + + - + - + - + + +All triples with a ‘+’ in front will be inserted and all triples preceded by ‘-’ are triples that will be deleted by the query. + +## What are the benefits? + +### Graph store independence +The delta-service has the obvious benefit that the framework can make abstraction from the graph store you are actually using. For instance, on a Virtuoso graph database both update (INSERT, DELETE) and select queries (SELECT, ASK, DESCRIBE) are sent to the same endpoint: [http://localhost:8890/sparql](http://localhost:8890/sparql) by default. On an OWLIM database on the other hand update queries are sent to [http://localhost:8001/openrdf-workbench/\[repository\]/statements](http://localhost:8001/openrdf-workbench/%5Brepository%5D/statements) while a select query is sent to [http://localhost:8001/openrdf-workbench/\[repository\]/query](http://localhost:8001/openrdf-workbench/%5Brepository%5D/query). The delta-service may even support idiosyncracies of a certain triple store like reforming queries if you know that the graph store cannot properly handle a specific query. + +### Messaging +Additionally a service that can notify interested parties of the changes in the graph store can easily replace messages and message queues. While message queues do scale, they still require a shared mental model between the producing and the consuming microservices. This mental model is enforced by most messaging systems while with the delta-service you are free to subscribe to whatever you want. As long as it is expressible in a triple, a microservice can hook into it. + +The producing microservice does not need to compose a specific message for the message queue. It just writes the triples to the triple store as it normally would (within the [mu.semte.ch](http://mu.semte.ch/) framework the triple store holds the ground truth) and the delta-service takes care of the rest. So here the win is: no mental model change.  Also the consuming microservice on the other hand does not need to know the transformation model the producing microservice uses to transform the message to the message queue format. Again it only needs to know triples. + +And here we win a lot with the added semantic value of the controlled vocabularies used with linked data technology. For example should I install a microservice on my platform that performs actions every time a concept scheme is added to the SPARQL store, then that microservice just needs to listen to all changes on URIs that are a SKOS:ConceptScheme. + +In short, while you can build a message queue with this technology the messages would always handle a view of the model but by being able to consume the delta reports all the reactiveness is being leveraged by nothing but manipulating the model. + +### Scaling +The delta-service may also facilitate the scaling of the mu.semte.ch platform. If we would introduce a master graph database instance to which all updates (but preferably no selects) are proxied then we could put a delta-service in front of this master graph database and send mutation reports of the master graph database to a slave battery updater. This slave battery updater can then update an array of slave stores with every report. The slaves can be used for select queries. This way we have free scaling of the graph database and even more: we can have multiple different types of graph databases all in sync. + +### Reactiveness +The delta-service enables us to use a new approach to developing applications: reactive programming. Make a microservice hook into changes produced by another microservice. Have a look at [the blog posts we’ve already published on this topic](https://mu.semte.ch/tag/reactive-programming/) to learn how to make a microservice reactive. + +## Conclusion +The delta-service is just a microservice, but one that offers a lot of benefits. In the future we will publish more blog posts illustrating how you can use the delta-service in your mu.semte.ch project and easily benefit from the difference reports it generates. \ No newline at end of file From 2361366f33c8d5031dbdbe46f8ca620047e56bf1 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 17:26:38 +0100 Subject: [PATCH 23/52] Archived Auto-expanding uploaded semantic files --- .../auto-expanding-uploaded-semantic-files.md | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 archive/auto-expanding-uploaded-semantic-files.md diff --git a/archive/auto-expanding-uploaded-semantic-files.md b/archive/auto-expanding-uploaded-semantic-files.md new file mode 100644 index 0000000..7269c52 --- /dev/null +++ b/archive/auto-expanding-uploaded-semantic-files.md @@ -0,0 +1,43 @@ +**Editors note** +The following has been archived due to delta-service being superseded by delta-notifier. More info on the deprecation can be found in the [delta-service repo](https://github.com/mu-semtech/archived-mu-delta-service) + +*This document has been adapted from Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/06/01/auto-expanding-uploaded-semantic-files/)* + +--- + +# Auto-expanding uploaded semantic files + +By adding 2 new microservices to our regular [mu.semte.ch](http://mu.semte.ch/) setup ([https://github.com/mu-semtech/mu-project](https://github.com/mu-semtech/mu-project)) we can create a very nifty workflow that will automatically expand semantic files in our graph database. + +## File uploader service +We need a file uploader service that after accepting a POST with a file saves that![](http://mu.semte.ch/wp-content/uploads/2017/05/fileservice-200x300.png) file and adds information about that file in our triple store. This information can be expressed using the following rather limited vocabulary: +A file is of class [http://mu.semte.ch/vocabularies/file-service/File](http://mu.semte.ch/vocabularies/file-service/File) +and has the following properties + +* [http://mu.semte.ch/vocabularies/file-service/internalFilename](http://mu.semte.ch/vocabularies/file-service/internalFilename) +* [http://mu.semte.ch/vocabularies/file-service/filename](http://mu.semte.ch/vocabularies/file-service/filename) +* [http://mu.semte.ch/vocabularies/file-service/uploadedAt](http://mu.semte.ch/vocabularies/file-service/uploadedAt) +* [http://mu.semte.ch/vocabularies/file-service/status](http://mu.semte.ch/vocabularies/file-service/status) + +## Semantic expander service +Next we need to have a semantic expander service. This service is a little bit more complicated because it handles 2 separate functionalities. + +The first functionality that this service should have is support to consume delta’s as they are generated by the delta service ([https://github.com/mu-semtech/mu-delta-service](https://github.com/mu-semtech/mu-delta-service)). In these reports we will need to filter out the files that contain semantic data and whose status has changed to uploaded. We can achieve this in a rather brute way by first making a set of all URIs of the subjects in the insert reports. After this we can make a simple query that looks somewhat like this: + +``` +SELECT DISTINCT ?internal_filename + WHERE { + ?uri a http://mu.semte.ch/vocabularies/file-service/File . + ?uri http://mu.semte.ch/vocabularies/file-service/internalFilename ?internal_filename . + ?uri http://mu.semte.ch/vocabularies/file-service/filename ?filename . + FILTER(strends(?filename, “.ttl”) || ?filename, “.rdf”)) + FILTER(?uri in ([LIST OF ALL THE URI’s FOUND])) + FILTER NOT EXISTS { + ?uri http://mu.semte.ch/vocabularies/semantic-expander/expanded ?date + } + } +``` + +This query will provide us with a list of filenames. We can now expand each of theses filenames. This can be done either (1) by converting the files to one or more insert queries, (2) by using a graph protocol to load an entire file or (3) by using store specific logic to load the files (i.e. using iSQL on Virtuoso to create a load list and then starting the load endpoint). + +And tada! Whenever we upload a file with semantic content to our backend, the semantic expander service will pick it up automatically and load the contents in the triple store. Almost magic. \ No newline at end of file From e98389bf840e660407faf2a5a9fef1f2a1301492 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 17:38:52 +0100 Subject: [PATCH 24/52] Imported Thoughts on how a distributed SPARQL endp --- ...ed-SPARQL-endpoint-might-be-implemented.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 archive/thoughts-on-how-a-distributed-SPARQL-endpoint-might-be-implemented.md diff --git a/archive/thoughts-on-how-a-distributed-SPARQL-endpoint-might-be-implemented.md b/archive/thoughts-on-how-a-distributed-SPARQL-endpoint-might-be-implemented.md new file mode 100644 index 0000000..94b6cb1 --- /dev/null +++ b/archive/thoughts-on-how-a-distributed-SPARQL-endpoint-might-be-implemented.md @@ -0,0 +1,47 @@ +**Editors note** +The following has been archived due to delta-service being superseded by delta-notifier. More info on the deprecation can be found in the [delta-service repo](https://github.com/mu-semtech/archived-mu-delta-service) +Also note that this is a direct import of the article written in 2017, before the master/slave terminology that is used within has come under larger scrutiny. I (the editor/importer) nor the person who originally wrote the article have made recent changes on it, and it has been asked to be left as-is, albeit archived. + +*This document has been adapted from Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/10/26/thoughts-on-how-a-distributed-sparql-endpoint-might-be-implemented/)* + +--- + +# Thoughts on how a distributed SPARQL endpoint might be implemented +![](http://mu.semte.ch/wp-content/uploads/2017/10/replications.png) + +The problem with most triple stores is … they tend to be slow. At least slow compared to their schema-full counterparts. It seems the cost of not defining a fixed schematic structure upfront is a not easy to avoid. We have been thinking about various models and solutions to mitigate this issue for specific applications. With the simplicity of the [mu.semte.ch](http://mu.semte.ch/) stack it is easy to add caches left and right but in the end that does not solve all problems. This post is intended to present an idea on how to add a solution by making the triple store distributed. + +## What kind of distributed? +There are various models of distributed stores and maybe we explore other in subsequent posts.  In this post we present one way of distributing a triplestore across multiple instances. + +The high-level idea is that a delta-service detects changes which would be introduced by update-queries.  These updates are dispatched to a set of replicated triplestores.  Queries and updates are ordered correctly to ensure updates are visible when a combination of read and write queries are executed. + +## Terminology +A quick overview of the terminology used below as it is quite ambiguous. + +- query: a SPARQL query that is not an insert or delete query. +- update: a SPARQL query that is an insert, delete or delete-insert-where query. +- SPARQL query: a query or an update. + +## Architecture +A DSE (Distributed Sparql Endpoint) consists of a distributed SPARQL Endpoint Controller, a delta calculating microservice, a slave controller microservice, optionally a base slave store and then one or more slaves stores. + +![](http://mu.semte.ch/wp-content/uploads/2017/10/distributedSPARQLEndpoint-1.png) + +## Distributed SPARQL Endpoint Controller (DSEC) +The DSEC acts as the entrypoint for the DSE. The most basic form of its functionality would be to split the incoming SPARQL queries into queries and updates. Although updates change the database’s state, the DSEC is itself is almost fully stateless.  A notable exception is an increasing number attached to each incoming SPARQL query. This number allows us to prevent the execution of queries before all updates that were sent earlier have been applied. + +## Delta Calculating Microservice +The delta microservice calculates which triples are changed based on a received query.  It has three distinct responsibilities: + +1. Maintain a queue of pending updates; +2. Calculate the delta that would be introduced by executing the \[lowest\] update on the queue; +3. Execute those delta’s on it’s own \[master\] data store. + +*Note: The Delta Calculating Microservice could maintain it’s state in a private SPARQL store to facilitate rebooting the component without loss of state should it go down. Rolling back should not happen as any update would be translated into an update of the form `WITH INSERT DATA { … triple … }`. In this case we can assume the underlying triple store to handle the cases where roll back is needed.* + +## Slave Master Microservice +The Slave Master Microservice maintains the slaves stores’ lifecycle including creation and termination. This microservice also manages a queue of queries and uses a load balancing algorithm to assign the query to a slave. When the Delta Calculating Microservice sends out a delta report the Slave Master Microservice will inform all slaves of the change in state (in essence nothing more than running the above mentioned ‘simple’ update). The Slave Master knows the latest update number for each slave and can take this into account before sending queries. + +## Conclusion +The ideas in this post build heavily on the reactive programming ideas presented in earlier posts. The main weakness of this approach is still the single point of failure that is the delta calculating component itself. This component could be made distributed by further enhancements.  It is currently the only component that processes update queries in this ecosystem. Maybe for a system with low write and high read access this would be a valuable asset. Keep tuned for the PoC! From 11b49ff28a713a0edf4350bde835b94eab7aa5ce Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 17:46:23 +0100 Subject: [PATCH 25/52] Imported Publications from mu.semte.ch/components/ --- publications.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 publications.md diff --git a/publications.md b/publications.md new file mode 100644 index 0000000..f82982a --- /dev/null +++ b/publications.md @@ -0,0 +1,39 @@ +# Publications + +## State-of-the-art web applications fuelled by Linked Data aware microservices + +*Presentation in the Industry Track of [SEMANTICS](http://2016.semantics.cc/), September 13th, 2016, Leipzig, Germany* + +The presentation introduces and discusses the mu.semte.ch framework. Our experience so far shows that the framework offers a productive way of quickly developing practical applications on top of Linked Data covering various situations and domains. Code can be extensively shared across projects in the frontend as well as in the backend which shortens the application development time drastically. + +[Read](http://www.slideshare.net/AadVersteden/musemtech-a-journey-from-tenforces-perspective-semantics2016) + + + +## State-of-the-art Web Applications using Microservices and Linked Data + +*Paper for the [4th SALAD workshop](http://salad2016.linked.services/) at ESWC, May 29th, 2016, Heraklion, Crete* + +Description of mu.semte.ch, a platform for building state-of-the-art web applications fuelled by Linked Data aware microservices. The platform assumes a mashup-like construction of single page web applications which consume various services. In order to reuse tooling built in the community, Linked Data is not pushed to the frontend. + +[Read](http://ceur-ws.org/Vol-1629/paper4.pdf) + + + +## An Ecosystem of User-facing Microservices supported by Semantic Models + +*Position paper for the [5th International USEWOD Workshop](http://usewod.org/usewod2015.html) at ESWC, May 31st, 2015, Portoroz, Slovenia* + +Microservices promise to vastly simplify application complexity. Tiny applications offering +very specific functionality are easy to build and maintain. But how do you keep your +models in sync? Is there a way to share the microservices between parties? By leveraging +the power of semantic technologies, we can create an ecosystem in which many +microservices complete a unified model. A small semantic web to be used within an +organization. This lets organizations have a taste of semantic technologies cheaply with +minimal risk. Our strategy gives focus on supporting single page web applications, where +javascript and REST API’s are king. In this short position paper we describe an +architecture for building, combining and sharing user-facing microservices for state-of-the- +art web applications. + +[Read](http://usewod.org/files/workshops/2015/papers/USEWOD15_versteden_pauwels_papantaniou.pdf) + From 4eee2da66c2d360ee85d0022ec71f8027d390355 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 17:49:52 +0100 Subject: [PATCH 26/52] Imported mu.semte.ch/who/ --- writeups/who---mu-semtech.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 writeups/who---mu-semtech.md diff --git a/writeups/who---mu-semtech.md b/writeups/who---mu-semtech.md new file mode 100644 index 0000000..f0c5e23 --- /dev/null +++ b/writeups/who---mu-semtech.md @@ -0,0 +1,19 @@ +# Who + +## … governs mu.semte.ch? + +The project is disconnected from individual commercial entities ensuring it stays free to use.  The core team meets every two weeks and discusses raised topics whilst prioritising and adding new features. + +![](http://mu.semte.ch/wp-content/uploads/2017/05/erika.jpg) +![](http://mu.semte.ch/wp-content/uploads/2017/05/jonathan.jpg) +![](http://mu.semte.ch/wp-content/uploads/2017/05/aad.jpg) +![](http://mu.semte.ch/wp-content/uploads/2017/05/felix.jpg) + +## … uses mu.semte.ch? +![](http://mu.semte.ch/wp-content/uploads/2017/05/fabbrikka.png)![](http://mu.semte.ch/wp-content/uploads/2017/05/european-commission-e1496161303799.jpg) + +![](http://mu.semte.ch/wp-content/uploads/2017/05/pintafish-1024x1024.jpg)![](http://mu.semte.ch/wp-content/uploads/2017/04/your-data-stories.png)![](http://mu.semte.ch/wp-content/uploads/2017/05/BDE_vertical_noslogan.png) + +![](http://mu.semte.ch/wp-content/uploads/2017/05/vlaamse-overheid.jpg)![](http://mu.semte.ch/wp-content/uploads/2017/05/bravoer.png) + +![](http://mu.semte.ch/wp-content/uploads/2017/05/veeakker.png)![](http://mu.semte.ch/wp-content/uploads/2017/04/moof.jpg) From 2474862d1d2f4ec458595f7b85b73693b59265da Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 17:53:15 +0100 Subject: [PATCH 27/52] Imported mu.semte.ch/about/ --- README.md | 1 + docs/discussions/mu-semtech-primer.md | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 docs/discussions/mu-semtech-primer.md diff --git a/README.md b/README.md index 995ee7a..702996b 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ If you want more information behind the design of semantic.works, you can read t - **Why semantic...** - [... technology?](docs/discussions/why-semantic-tech.md) - [... microservices?](docs/discussions/why-semantic-microservices.md) +- [mu.semte.ch primer](docs/discussions/mu-semtech-primer.md) - **Experimentation** - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) diff --git a/docs/discussions/mu-semtech-primer.md b/docs/discussions/mu-semtech-primer.md new file mode 100644 index 0000000..879775d --- /dev/null +++ b/docs/discussions/mu-semtech-primer.md @@ -0,0 +1,17 @@ +# mu.semte.ch primer (About) +Microservices are a good idea, if only they were easy to understand and integrate.  The answer is here, mu.semte.ch.  Your framework for building microservices that are easy to install, integrate, and reuse.  All microservices are reusable in the backend, the frontend integrates everything into a unified look-and-feel. + +## How do we do it? +We cheat.  Instead of letting microservices depend on each other, we lean on the semantic web for data integration.  The semantic web, as envisioned by the W3C, allows computers from all over the world to form one gigantic database.  All without really knowing about each other.  As that looks like a nice feature for microservices, we used our extensive knowledge on semantic technologies to let microservices interoperate using these models.  Only using the database as a syncing point. + +## Deployment? +We tackle that using Docker and Docker Compose.  It’s simple to understand, and trivial to get running.  We use the docker-compose.yml file to describe the main topology, containing all microservices in your stack.  With a single command, you can download all the microservices of your stack, and start them up.  This makes deployment a fun and easy task. + +## Reuse? +Many backend services are easy to reuse.  We have services available for login, registration, regular resource listing and creation (mu-cl-resources), migrations, exports, …  But the real gem lies in how to roll your own.  We have templates for various languages, like JavaScript and Ruby, making it trivial to build your own microservice. + +## Integration? +The look and feel of an application is important.  It needs to look nice and integrated, but it also needs to be cheap.  Mass-production, but custom.  We integrate all microservices in a Single Page App.  We standardize on EmberJS, the most stable choice for frontend development and reuse.  Its structure makes it easy for us to reuse know-how in disconnected projects.  It also allows us to share frontend logic, and sometimes visualisation, using addons.  In the past three years, it has been the logical step forward. + +## Sustainability? +After open sourcing our hobby project, various entities have contributed to mu.semte.ch.   As such, we are being used at the European Commission, in various research institutes, at the Flemish government, and even by some SMEs.  We are actively working together with each of them, to ensure the platform is used optimally, and in line with our future vision. From 0f0b07fffe4be530effa201f6a26cf944b639af6 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 17:58:07 +0100 Subject: [PATCH 28/52] Changed link from blogpost to documentation --- docs/discussions/experimentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/discussions/experimentation.md b/docs/discussions/experimentation.md index b7594d2..916f867 100644 --- a/docs/discussions/experimentation.md +++ b/docs/discussions/experimentation.md @@ -61,7 +61,7 @@ and then you add a app.js file where you enter something like With this you have your first javascript microservice that will respond to the ‘/’ route with a simple hello world string (I know not entirely true, you will also need to configure the dispatcher but for that look at our docs on [mu.semte.ch](http://mu.semte.ch/)). After this you can go wild with javascript and you don’t have to worry about the boring stuff that make the webservice tick, allow you to write/read to the database, etc. ### Other languages -If we compare this with the [ruby template](https://mu.semte.ch/2017/04/13/building-your-first-microservice-with-sinatra/) then we find that for ruby your FROM statement is slightly different (you guess what changes) and instead of a app.js file you now create a web.rb file and again the simplest form looks almost like the example above. +If we compare this with the [ruby template](https://github.com/mu-semtech/mu-ruby-template#building-your-first-microservice-with-sinatra/) then we find that for ruby your FROM statement is slightly different (you guess what changes) and instead of a app.js file you now create a web.rb file and again the simplest form looks almost like the example above. Suppose you want to go for python? Well exactly the same. From e06005a0f96552172a3d892495addea952bffa16 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 26 Jan 2023 18:02:24 +0100 Subject: [PATCH 29/52] renamed how-to to how-tos --- docs/{how-to => how-tos}/developing-inside-containers.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{how-to => how-tos}/developing-inside-containers.md (100%) diff --git a/docs/how-to/developing-inside-containers.md b/docs/how-tos/developing-inside-containers.md similarity index 100% rename from docs/how-to/developing-inside-containers.md rename to docs/how-tos/developing-inside-containers.md From 320d49777e85d38e6ab614f0c62eec2402ac63e7 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 9 Mar 2023 14:37:09 +0100 Subject: [PATCH 30/52] Imported How to build a microservice template --- .../How to build a microservice template.docx | Bin 0 -> 233937 bytes docs/references/template-requirements.md | 96 +++++++++ docs/tutorials/building-a-template.md | 182 ++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 archive/How to build a microservice template.docx create mode 100644 docs/references/template-requirements.md create mode 100644 docs/tutorials/building-a-template.md diff --git a/archive/How to build a microservice template.docx b/archive/How to build a microservice template.docx new file mode 100644 index 0000000000000000000000000000000000000000..409a23b347c278aedeccae94b1a2a927caaf6f2c GIT binary patch literal 233937 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ⅇEIN()i-=E|7Q(4C!Xm)c{=fL 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 DiZ{BG literal 0 HcmV?d00001 diff --git a/docs/references/template-requirements.md b/docs/references/template-requirements.md new file mode 100644 index 0000000..0f742f1 --- /dev/null +++ b/docs/references/template-requirements.md @@ -0,0 +1,96 @@ +# Template requirements + +## PoC (proof of concept) requirements +- Respond to HTTP requests + - Parse JSON & JSONAPI payloads + - Support implementation of GET PUT PATCH POST DELETE + - On port 80 during production + +- Send out SPARQL queries & updates over HTTP + - Perform JSON request to SPARQL HTTP endpoint + - Parse JSON query response + - Support Virtuoso Open Source [[1]](#1) + +- Generate HTTP response + - Build JSON & JSONAPI body + - Set outgoing HTTP headers + +- User-code + - Load user-code from `/app` + - Download & install dependencies on startup + +## Full template requirements + +- [**All requirements for the PoC**](#poc-proof-of-concept-requirements) + +- Configuration + - Access environment variables [[2]](#notes) + - Support live-reload for development [[3]](#notes) + +- Respond to HTTP requests + - Parse incoming HTTP headers + - Destructure request URL + - Parse query parameters [[4]](#notes) + +- Send out SPARQL queries & updates over HTTP + - Set headers of the SPARQL HTTP request [[5]](#notes) + - Provide escaping methods for SPARQL input + +- User-code + - Load user configuration code from /config + - Use ONBUILD to automate template extension [[6]](#notes) + - Support the generation of UUIDs, namely uuid-v1 + +- Enhancing workflow + - Use /template-name/setup.sh for downloading all dependencies + > into the image[[7]](#notes) + - Use /template-name/startup.sh for launching the webserver. + - Use the ENVIRONMENT variable to enable development mode & + > live-reload[[8]](#notes) + - Debugging console (if possible) + - Enable debugging symbols (if possible) + - Enable error output (if configurable) + +## Notes + +1. This constraint should be dropped in the distant future, but it is + > our default choice at the time. If you don\'t do anything special, + > this will be ok. + +2. Docker makes it easy to set POSIX environment variables. These are + > the variables you\'d be able to access in a shell (\$PATH is a + > good example). These should be accessible so services can override + > your service. + +3. Live-reload does not exist on all systems, but you can almost always + > fake it. Some languages need a restart (like nodejs and ruby), for + > some others you should connect to the service and reload the code + > through your editor (like Common Lisp with slime). This reloading + > does not need to auto-reload the dependencies, as these rarely + > change and thus warrant restarting the service. + +4. Query parameters is the content that goes behind the question mark + > (?) in the URL. It constitutes of key-value pairs in which the + > keys needn\'t be unique. + +5. This requirement comes forth from the work Jonathan Langens is + > performing on Sharing Knowledge (link should be added to basic + > ground work in the field). + +6. The ONBUILD statement of Docker allows you to execute commands when + > someone extends your image. This means you can automatically copy + > the relevant contents of the repository in your /app and your + > /config. This further reduces the overhead of implementing a new + > template. + +7. This construction makes it predictable how extensions of this + > template should behave. If someone builds a template on top of + > your template (inception-style), then it\'s easily understandable + > what needs to be called. + +8. It may be that development mode can not be enabled using an + > environment variable as it requires too many extra dependencies. + > This is strongly advised, but it is an option. In that case, the + > development image should be maintained in the same repository as + > the template repository. The name of the development should be + > semtech/\-dev diff --git a/docs/tutorials/building-a-template.md b/docs/tutorials/building-a-template.md new file mode 100644 index 0000000..4a0fc2a --- /dev/null +++ b/docs/tutorials/building-a-template.md @@ -0,0 +1,182 @@ +# So you want to build a template? +An explanation and motivation for templates, from a producer's point of view + +This specification describes the mindset we have for each mu.semte.ch +template. It describes the goals of a template, the soft constraints, +hints at method naming, and lists key features which a template should +have. + +## Why templates + +Templates make it easier to implement new microservices. They help you +get started with the technology you know and love, or with a new +technology. + +Templates should be predictable. The naming and structure of templates +should therefore match certain standards. Templates make it easy to get +started with the technology you know and love, or help you understand +the structure of a service written in a language you barely know. + +Templates make it easy to follow the constraints mu.semte.ch places on +services. They should therefore contain a basic set of features which +ensures users can easily develop applications in them. + +Templates are our way to familiarize users with mu.semte.ch and to +ensure users follow the best practices where possible. + +## Template features + +A template should make sure that the mu.semte.ch ecosystem keeps +running. The construction of a template happens in a set of stages. +First one should implement a basic service in the base technology, to +get a feeling of which technologies are suited for the template. Next up +is the construction of a PoC (Proof of Concept). Then comes the +standardisation, which requires some extra features to ensure the +template can be used in practice. + +You can check the requirements for the PoC [here](../references/template-requirements.md#poc-proof-of-concept-requirements) + +And the requirements for the full template [here](../references/template-requirements.md#full-template-requirements) + + +## Levels of abstraction + +Most developers like to abstract things. Make sure that nothing is +repeated. Many of us like to get started quickly. These two ideas often +don't go hand-in-hand. We dismiss non-generic abstractions and require +consistent naming. + +Getting started should be trivial and has the highest priority. It +should be intuitive to start using a new template. It should be easy to +read code that was written in a template, even if it isn't yours. + +The following is a description of various topics on which consistency +should be held. The description should be adapted to the use of your +language. Ie: if you'd call sparql_query in Ruby, sparqlQuery in Java +and sparql-query in Common Lisp. + +### Defining HTTP endpoints + +Users will define HTTP endpoints to which their service will respond. +Separate methods should be defined for separate types of call. The +method should simply contain the name of HTTP verb, and the URL to which +we respond. The system should allow the user to destructure the input +and access the destructured contents. Calls may be defined in a block, +but if that is the case, then the block should be repeatable. + +**Essentials:** +- Use simple name of HTTP verb +- Allow straight-forward destructuring of URL +- Support finding query parameters + +Example in Common Lisp: + +```lisp +(defcall :get (“say” “hello” “to” name) + ;; responds to GET /say/hello/to/Aad?title=Sir + ;; name is bound to Aad + ;; (get-parameter “title”) yields “Sir” +) + +``` + +Example in Ruby: +```ruby +get “say/hello/to/:name” do |name| + # responds to GET /say/hello/to/Erika?title=Madam + # name is bound to erika + # params[:title] yields “Madam” +end + +``` + +Another example in Ruby: +```ruby +define_calls do |server| + server.get “say/hello/to/:name” do |name| + # responds to GET /say/hello/to/Erika?title=Madam + # name is bound to erika + # params[:title] yields “Madam” + end +end +``` + +### Executing SPARQL queries + +We should mitigate SPARQL-injection whilst still ensuring the SPARQL +queries are easy to recognise. Allow for injecting variables in a +SPARQL-query string. Support escaping as separate methods. Query results +should be parsed automatically so the user can easily process them. + +A SPARQL endpoint often differs in query and update endpoints. Hence we +advise to use those two names for executing the queries. + +For languages which don't allow injecting variables into strings, magic +is allowed. Keep it to an absolute minimum and try to mimic what is +commonly found in other languages. It should be something which you can +explain in a single sentence. + +Common Lisp example: +```lisp +(sparql-query “SELECT ?s ?p ?o WHERE + GRAPH { + ?s a foaf:Agent; + foaf:name ~A; + ?p ?o. + }”, (sparql-escape-string “Felix”)) + +(sparql-update “INSERT DATA + GRAPH { + ext:Aad a foaf:Agent. + }”) +``` +J + +Ruby example: +```ruby +query “SELECT ?s ?p ?o WHERE + GRAPH { + ?s a foaf:Agent; + foaf:name #{username.sparql_escape}; + ?p ?o. + }” +``` + +## Getting started + +Building a template without knowing the domain is near impossible. The +following steps guide you into building a usable template. + +First select the technology you would like to support. Look around for +web frameworks which match the ideology of microservices. Search for the +'Ruby Sinatra' competitor for your language. + +Your next challenge is to implement a simple microservice for this +purpose. As an example, you can write a service that returns the amount +of Tasks in the store, and which allows you to set a Task as done. The +service itself is not of importance, you'll learn lessons about the +framework when you implement the microservice. + +Don't forget to run your microservice in a toy mu.semte.ch project. +That way you'll know that it actually works. +[Mu-project](https://github.com/mu-semtech/mu-project) +offers a good starting point. + +Once your service works, you can start abstracting it. Much of the code +you've built would be used again when you implement another service in +the same language. Go over the list of the PoC, ensure you've +implemented each feature, and put this in a separate project. This +project will become the template to use for the Tasks service. If all +goes well, you'll end with a PoC template service, and a much shorter +Tasks service. + +Next up is going over the list of features needed for the full template. +Go over the list of features and try to implement them in a clean +fashion. More complex, and realistic microservices, like those +automatically creating Tasks for all starred books you haven't read yet +now become possible. This is the space where your microservice should be +in. + +Document the use of the microservice, and get us to publish it on our +GitHub space. + From 0e3e8755d281c74f698405839d70573df1726ec6 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 9 Mar 2023 14:47:06 +0100 Subject: [PATCH 31/52] Fixed template documentation formatting --- docs/references/template-requirements.md | 59 ++++++++++++------------ docs/tutorials/building-a-template.md | 5 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/references/template-requirements.md b/docs/references/template-requirements.md index 0f742f1..40b03dd 100644 --- a/docs/references/template-requirements.md +++ b/docs/references/template-requirements.md @@ -42,55 +42,56 @@ - Support the generation of UUIDs, namely uuid-v1 - Enhancing workflow - - Use /template-name/setup.sh for downloading all dependencies - > into the image[[7]](#notes) + - Use /template-name/setup.sh for downloading all dependencies + into the image [[7]](#notes) - Use /template-name/startup.sh for launching the webserver. - Use the ENVIRONMENT variable to enable development mode & - > live-reload[[8]](#notes) + live-reload [[8]](#notes) - Debugging console (if possible) - Enable debugging symbols (if possible) - Enable error output (if configurable) + ## Notes 1. This constraint should be dropped in the distant future, but it is - > our default choice at the time. If you don\'t do anything special, - > this will be ok. + our default choice at the time. If you don\'t do anything special, + this will be ok. 2. Docker makes it easy to set POSIX environment variables. These are - > the variables you\'d be able to access in a shell (\$PATH is a - > good example). These should be accessible so services can override - > your service. + the variables you\'d be able to access in a shell (`$PATH` is a + good example). These should be accessible so services can override + your service. 3. Live-reload does not exist on all systems, but you can almost always - > fake it. Some languages need a restart (like nodejs and ruby), for - > some others you should connect to the service and reload the code - > through your editor (like Common Lisp with slime). This reloading - > does not need to auto-reload the dependencies, as these rarely - > change and thus warrant restarting the service. + fake it. Some languages need a restart (like nodejs and ruby), for + some others you should connect to the service and reload the code + through your editor (like Common Lisp with slime). This reloading + does not need to auto-reload the dependencies, as these rarely + change and thus warrant restarting the service. 4. Query parameters is the content that goes behind the question mark - > (?) in the URL. It constitutes of key-value pairs in which the - > keys needn\'t be unique. + (?) in the URL. It constitutes of key-value pairs in which the + keys needn\'t be unique. 5. This requirement comes forth from the work Jonathan Langens is - > performing on Sharing Knowledge (link should be added to basic - > ground work in the field). + performing on Sharing Knowledge (link should be added to basic + ground work in the field). 6. The ONBUILD statement of Docker allows you to execute commands when - > someone extends your image. This means you can automatically copy - > the relevant contents of the repository in your /app and your - > /config. This further reduces the overhead of implementing a new - > template. + someone extends your image. This means you can automatically copy + the relevant contents of the repository in your /app and your + /config. This further reduces the overhead of implementing a new + template. 7. This construction makes it predictable how extensions of this - > template should behave. If someone builds a template on top of - > your template (inception-style), then it\'s easily understandable - > what needs to be called. + template should behave. If someone builds a template on top of + your template (inception-style), then it\'s easily understandable + what needs to be called. 8. It may be that development mode can not be enabled using an - > environment variable as it requires too many extra dependencies. - > This is strongly advised, but it is an option. In that case, the - > development image should be maintained in the same repository as - > the template repository. The name of the development should be - > semtech/\-dev + environment variable as it requires too many extra dependencies. + This is strongly advised, but it is an option. In that case, the + development image should be maintained in the same repository as + the template repository. The name of the development should be + `semtech/-dev` diff --git a/docs/tutorials/building-a-template.md b/docs/tutorials/building-a-template.md index 4a0fc2a..c640267 100644 --- a/docs/tutorials/building-a-template.md +++ b/docs/tutorials/building-a-template.md @@ -34,9 +34,8 @@ is the construction of a PoC (Proof of Concept). Then comes the standardisation, which requires some extra features to ensure the template can be used in practice. -You can check the requirements for the PoC [here](../references/template-requirements.md#poc-proof-of-concept-requirements) - -And the requirements for the full template [here](../references/template-requirements.md#full-template-requirements) +- **You can check the requirements for the PoC [here](../references/template-requirements.md#poc-proof-of-concept-requirements)** +- **And the requirements for the full template [here](../references/template-requirements.md#full-template-requirements)** ## Levels of abstraction From 4db6dea24b7075e38dc2fca60b8db0695029506a Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 9 Mar 2023 16:05:21 +0100 Subject: [PATCH 32/52] Documented helper functions, added header table --- docs/references/template-requirements.md | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/references/template-requirements.md b/docs/references/template-requirements.md index 40b03dd..9e6bf80 100644 --- a/docs/references/template-requirements.md +++ b/docs/references/template-requirements.md @@ -51,6 +51,50 @@ - Enable debugging symbols (if possible) - Enable error output (if configurable) +## Helper functions +Ideally, your template should expose some helper functions, including +but not limited to the following. Extra parameters can be added as +needed or wanted. + + +| Function name | Parameters | Description | +| ------------------ | --------------------- | ----------- | +| error | title: `string`, http_error_code: `int` | Returns a JSONAPI compliant error response with the given status code (default: 400). | +| query | query: `string` | Executes the given SPARQL select/ask/construct query. [This function should automatically pass the correct headers](#passing-headers). Also see `update`. | +| session_id_header | request: `object` | Get the session id from the HTTP request headers. | +| sparql_escape | value: `any` | Detects the value type and runs the appropiate [sparql escape function](#sparql_escape). | +| update | query: `string` | Executes the given SPARQL update query. [This function should automatically pass the correct headers](#passing-headers). Also see `query`. | +| uuid | None | Generates an uuid. Meant for use in linked data (`mu:uuid`). | + +*Note: camelcase is used in this documentation. However, when writing the helpers in the language of your choice, use the notation that is in line with the language in question.* + +### sparql_escape +The following helper functions all have the same description/funcionality, but for different types. As such, the description column is omitted. + +**Description:** Converts the given object to a SPARQL-safe RDF object string with the right RDF-datatype. +This functions should be used especially when inserting user-input to avoid SPARQL-injection. + +| Function name | Parameters | +| ---------------------- | ----------------- | +| sparql_escape_string | value: `string` | +| sparql_escape_uri | value: `url` | +| sparql_escape_decimal | value: `decimal` | +| sparql_escape_int | value: `int` | +| sparql_escape_float | value: `float` | +| sparql_escape_date | value: `date` | +| sparql_escape_datetime | value: `datetime` | +| sparql_escape_bool | value: `bool` | + +### Passing headers +The following headers should be passed when making queries: + +| Header name | Type | Description | +| ---------------------- | -------- | ----------------------- | +| MU-SESSION-ID | | | +| MU-CALL-ID | | | +| MU-AUTH-ALLOWED-GROUPS | | (bidirectional) | +| MU-AUTH-USED-GROUPS | | | + ## Notes From 3cc218c190940c84a46b6da86efc6e7bf9428a29 Mon Sep 17 00:00:00 2001 From: Rich Date: Wed, 13 Sep 2023 15:38:23 +0200 Subject: [PATCH 33/52] Add How-to for troubleshooting slow starting containers --- README.md | 4 +-- ...ubleshooting---slow-starting-containers.md | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 docs/how-tos/troubleshooting---slow-starting-containers.md diff --git a/README.md b/README.md index 702996b..b5395e2 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ If you want more information behind the design of semantic.works, you can read t - [Supporting MacOS](docs/discussions/supporting-mac-os.md) ## How-to -- [Develop with your local IDE and tools inside a Docker container](docs/how-to/developing-inside-containers.md) - +- [Develop with your local IDE and tools inside a Docker container](docs/how-tos/developing-inside-containers.md) +- [Troubleshooting - Slow starting containers using 100% CPU](docs/how-tos/troubleshooting---slow-starting-containers.md) ## Writeups Our retrospectives on... diff --git a/docs/how-tos/troubleshooting---slow-starting-containers.md b/docs/how-tos/troubleshooting---slow-starting-containers.md new file mode 100644 index 0000000..1fec9a5 --- /dev/null +++ b/docs/how-tos/troubleshooting---slow-starting-containers.md @@ -0,0 +1,25 @@ +# Troubleshooting - Slow starting containers using 100% CPU + +Some docker images used in Semantic Works stacks, notably those based on sbcl (common lisp) and elixir images, are very slow and CPU intensive to start if the limits of open file descriptors are very high for the container. This leads to a process using 100% of a CPU for some time before that container becomes usable. This can be worked around by setting the defaults for new containers in the docker daemon config (/etc/docker/daemon.json (create it if it doesn't exist)): + +```json +{ + "default-ulimits": { + "nofile": { + "Hard": 104583, + "Name": "nofile", + "Soft": 104583 + } + } +} +``` + +Or, if you want these high defaults for some reason, you can set per-container limits in a docker-compose file for each of the mu-project services: + +```yml + ulimits: + nofile: + soft: 104583 + hard: 104583 +``` + From c0135b67b123ad03f0130e542a1fbbb14465ac78 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Fri, 22 Sep 2023 14:01:22 +0200 Subject: [PATCH 34/52] Small README.md changes, documented documentation - The documentation reference got refactored to a how-to quickstart - First draft of discussion - documentation structure --- README.md | 10 ++- docs/discussions/documentation-structure.md | 74 +++++++++++++++++++ .../documentation-quickstart.md} | 11 +++ 3 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 docs/discussions/documentation-structure.md rename docs/{references/documentation.md => how-tos/documentation-quickstart.md} (81%) diff --git a/README.md b/README.md index b5395e2..f1295d0 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ This repository is used to track issues that apply cross-services in the full se ## Reference For technical information in semantic.works, you can see the following references: - [Naming conventions](docs/references/naming-conventions.md) -- [Documentation](docs/references/documentation.md) -- [Represting logged in users](docs/references/representing-logged-in-users.md) +- [Documentation quickstart](docs/references/documentation-quickstart.md) +- [Representing logged in users](docs/references/representing-logged-in-users.md) ## Discussions If you want more information behind the design of semantic.works, you can read the following discussions: @@ -12,12 +12,14 @@ If you want more information behind the design of semantic.works, you can read t - [... technology?](docs/discussions/why-semantic-tech.md) - [... microservices?](docs/discussions/why-semantic-microservices.md) - [mu.semte.ch primer](docs/discussions/mu-semtech-primer.md) +- [semantic.works' documentation structure](docs/discussions/documentation-structure.md) - **Experimentation** - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) -- [Reactive programming](docs/discussions/reactive-programming.md) +- **The benefits of microservices through...** + - [Reactive programming](docs/discussions/reactive-programming.md) + - [Smaller & readable code](docs/discussions/smaller-readable-code.md) - [Sharing authorization](docs/discussions/sharing-authorization.md) -- [Smaller & readable code](docs/discussions/smaller-readable-code.md) - [Docker multi-stage builds benefits](docs/discussions/docker-multi-stage-builds.md) - [Supporting MacOS](docs/discussions/supporting-mac-os.md) diff --git a/docs/discussions/documentation-structure.md b/docs/discussions/documentation-structure.md new file mode 100644 index 0000000..71419f0 --- /dev/null +++ b/docs/discussions/documentation-structure.md @@ -0,0 +1,74 @@ +# Semantic.works Documentation + +Documentation is hard, but it is instrumental to helping your code be useful to people that aren't you, and that's a worthwhile pursuit. Even if you're making things just for yourself, the "people that aren't you" descriptor also includes you in a few years/months/weeks, who has no idea what you were thinking. To have our documentation be as useful and in-tune with the semantic technologies as possible, the following principles were outlined + +## Design principles +1. [*Don't Repeat Yourself.*](#1-dont-repeat-yourself) +2. [*All-encompassing.*](#2-all-encompassing) +3. [*Ease of use: writing.*](#3-ease-of-use-writing) +4. [*Ease of use: reading.*](#4-ease-of-use-reading) +5. [*Micro-first.*](#5-micro-first) +6. [*Non-proprietary.*](#6-non-proprietary) + + +### 1. Don't Repeat Yourself +If we're copying documentation/text across places, we're doing something wrong: +- This may cause inconsistencies and one version being out of date, breaching [(4) ease of use: readers](#4-ease-of-use-readers), as well as the [(2) all-encompassing](#2-all-encompassing) +- This requires extra upkeep/work, breaching [(3) ease of use: writers](#3-ease-of-use-writers) + +### 2. All-encompassing +Many documentations are incredibly lacking, as you've probably experienced over your time as a developer. The divio documentation standard, which you can view [on documentation.divio.com](https://documentation.divio.com/), attempts to clearly outline *what* should be documented, and *how* you can *organise* it. You can also read a [quick start in this repository's how-tos](../how-tos/documentation-quickstart.md). + +### 3. Ease of use: writing +Minimising maintenance for the people who make the code is essential. Lots of people do not have the time, resources, training, or quite simply the desire to write down docs. This is one of the reasons we stay away from external documentation sites like gitbook: having them sign in to a whole different platform to write the documentation for their code would be way too much friction. (This would also make us reliant on the external documentation provider, [breaching (6)](#6-non-proprietary), and would require navigating to an external location, breaching [(4) Ease of use: reading](#4-ease-of-use-reading)). + +### 4. Ease of use: reading +- We have a bunch of projects going on, written in Ruby, Lisp, Elixer, JavaScript, Python... The documentations should nevertheless resemble eachother and be consistent. This is additionally possible thanks to the the divio documentation standard, as seen in [(2) all-encompassing](#2-all-encompassing) +- End-users should not have to jump through hoops to read the documentation. If you have a local clone of a repository, you should not be required to have an active internet connection to read its documentation. + +### 5. Micro-first +Semantic.works is about microservices, so that means a bunch of documentation will be **small**. This means it is important to not *have to* adhere to the structures of big projects. A documentation folder for something that can be explained in one README is a no-go. Start from a single (README) file, and expand only if necessary. + +### 6. Non-proprietary +Our repositores and projects are made to work anywhere, so this should be the case for our documentation as well. Websites, services and hosts shutting down\* or changing their licenses\*\* is not unheard of. No editor lock-in, no host lock-in. + +\* +\*\* + + +## Implementation +To achieve all of the above goals, we have the following things in place + +### Write the documentation +All documentation ends up in one of the following places +- If it's about the semantic works stack in its entirety, it should go in the `project` repository (see: this document) +- If it's something useful for people getting started with the semantic works stack as a whole, it should go in the `mu-project` repository. +- If it's about a specific microservice or project, it should go inside of that projects repository + +The documentation should ideally be written as presented in [project/how-tos/documentation-quickstart](../how-tos/documentation-quickstart.md). This also covers how to tag a new revision + +For microservice developers and most people working on semantic.works, this step is already where it ends! This will ensure that most design principles are met. However, to have a more readily available & traditionally structured documentation, the following steps get executed. + +### Semantic.works +[View the fork on GitHub here](https://github.com/Denperidge-Redpencil/semantic.works) + +Semantic.works was (re-)developed to contain all necessary links & information about our repositories. However, we ensured that none of this would *not* be hardcoded into the site, thanks to... + +### App-mu-info +[View the fork on GitHub here](https://github.com/Denperidge-Redpencil/app-mu-info-rework) + +Which is a service that holds a linked-data database and an endpoint about repositories and their revisions. This includes links to the repos, images, as well as the contents of the documentation, both parsed and unparsed. And this service fetches its data dynamically thanks to... + +### Repo-harvester +[View the repo on GitHub here](https://github.com/mu-semtech/repo-harvester) + +Which is a microservice that - after some basic configuration - will collect & clone all repositories and images from a specified accounts & hosts. It collect all the information app-mu-info's data model needs, including having all documentation aggregated & parsed into the divio sections. The documentation stuff is thanks to... + +### Divio-docs-parser +[View the repo on GitHub here](https://github.com/Denperidge-Redpencil/divio-docs-parser) + +Which is a Python package that, when given a directory, will handle everything in terms of finding & parsing documents. It returns a class filled with easily useable and fully parsed Python dictionaries. + + + +*This document has been loosely adapted from Denperidge's learning writeup. You can view it [here](https://github.com/Denperidge-Redpencil/Learning.md/blob/main/Notes/docs.md)* diff --git a/docs/references/documentation.md b/docs/how-tos/documentation-quickstart.md similarity index 81% rename from docs/references/documentation.md rename to docs/how-tos/documentation-quickstart.md index 59cd6c9..7e3426b 100644 --- a/docs/references/documentation.md +++ b/docs/how-tos/documentation-quickstart.md @@ -46,3 +46,14 @@ Checklist: - [ ] Explains design decisions - [ ] Considers alternatives - [ ] Helps the reader make sense of things + + +## Releasing a new version +Once a new version of a semantic.works repository is made it should... +- Be tagged using `git tag` +- Have a DockerHub image with the same tag (NOTE: this happens automatically if your repository has a `.woodpecker/.tag.yml` configuration) + +*This will ensure that the documentations in app-mu-info get updated. See [project/discussions/how-tos/documentation-structure](../discussions/documentation-structure.md) for more info* + + + From 5a6e2d11a1b89b68b97dd98585c5e0d9247c7f36 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 5 Oct 2023 16:20:47 +0200 Subject: [PATCH 35/52] README update, fixed typos + expanded doc-structure --- README.md | 2 +- docs/discussions/documentation-structure.md | 33 ++++++++++----------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f1295d0..bd9413e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ This repository is used to track issues that apply cross-services in the full se ## Reference For technical information in semantic.works, you can see the following references: - [Naming conventions](docs/references/naming-conventions.md) -- [Documentation quickstart](docs/references/documentation-quickstart.md) - [Representing logged in users](docs/references/representing-logged-in-users.md) ## Discussions @@ -24,6 +23,7 @@ If you want more information behind the design of semantic.works, you can read t - [Supporting MacOS](docs/discussions/supporting-mac-os.md) ## How-to +- [Documentation quickstart](docs/how-tos/documentation-quickstart.md) - [Develop with your local IDE and tools inside a Docker container](docs/how-tos/developing-inside-containers.md) - [Troubleshooting - Slow starting containers using 100% CPU](docs/how-tos/troubleshooting---slow-starting-containers.md) diff --git a/docs/discussions/documentation-structure.md b/docs/discussions/documentation-structure.md index 71419f0..d507b21 100644 --- a/docs/discussions/documentation-structure.md +++ b/docs/discussions/documentation-structure.md @@ -1,6 +1,6 @@ # Semantic.works Documentation -Documentation is hard, but it is instrumental to helping your code be useful to people that aren't you, and that's a worthwhile pursuit. Even if you're making things just for yourself, the "people that aren't you" descriptor also includes you in a few years/months/weeks, who has no idea what you were thinking. To have our documentation be as useful and in-tune with the semantic technologies as possible, the following principles were outlined +Documentation is hard, but it is instrumental to helping your code be useful to people that aren't you, and that's a worthwhile pursuit. Even if you're making things just for yourself, the aforementioned "people that aren't you" descriptor also includes you in a few years, months or weeks. To have our documentation be as useful and in-tune as possible with the semantic technologies we craete, the following principles were outlined. ## Design principles 1. [*Don't Repeat Yourself.*](#1-dont-repeat-yourself) @@ -13,31 +13,31 @@ Documentation is hard, but it is instrumental to helping your code be useful to ### 1. Don't Repeat Yourself If we're copying documentation/text across places, we're doing something wrong: -- This may cause inconsistencies and one version being out of date, breaching [(4) ease of use: readers](#4-ease-of-use-readers), as well as the [(2) all-encompassing](#2-all-encompassing) +- This may cause inconsistencies and out of date versions, breaching [(4) ease of use: readers](#4-ease-of-use-readers), as well as [(2) all-encompassing](#2-all-encompassing) - This requires extra upkeep/work, breaching [(3) ease of use: writers](#3-ease-of-use-writers) ### 2. All-encompassing -Many documentations are incredibly lacking, as you've probably experienced over your time as a developer. The divio documentation standard, which you can view [on documentation.divio.com](https://documentation.divio.com/), attempts to clearly outline *what* should be documented, and *how* you can *organise* it. You can also read a [quick start in this repository's how-tos](../how-tos/documentation-quickstart.md). +Many documentations are incredibly lacking, as you've probably experienced over your time as a developer. The divio documentation standard, which you can view on [documentation.divio.com](https://documentation.divio.com/), attempts to clearly outline *what* should be documented, and *where* you can organise it. While I recommend viewing the talk and/or text on their website, a [quick start is included in this repository's how-tos](../how-tos/documentation-quickstart.md). ### 3. Ease of use: writing -Minimising maintenance for the people who make the code is essential. Lots of people do not have the time, resources, training, or quite simply the desire to write down docs. This is one of the reasons we stay away from external documentation sites like gitbook: having them sign in to a whole different platform to write the documentation for their code would be way too much friction. (This would also make us reliant on the external documentation provider, [breaching (6)](#6-non-proprietary), and would require navigating to an external location, breaching [(4) Ease of use: reading](#4-ease-of-use-reading)). +Minimising maintenance for the people who make the code is essential. Lots of people do not have the time, resources, training, or quite simply the desire to write down docs. This is one of the reasons we stay away from external documentation sites like gitbook: having them sign in to a whole different platform to write the documentation for their code would be way too much friction. This would also make us reliant on the external documentation provider, breaching [(6) Non-proprietary](#6-non-proprietary), and would require navigating to an external location for readers and writers alike, thus additionally breaching [(4) Ease of use: reading](#4-ease-of-use-reading). ### 4. Ease of use: reading -- We have a bunch of projects going on, written in Ruby, Lisp, Elixer, JavaScript, Python... The documentations should nevertheless resemble eachother and be consistent. This is additionally possible thanks to the the divio documentation standard, as seen in [(2) all-encompassing](#2-all-encompassing) +- We have a bunch of projects going on, written in Ruby, Lisp, Elixer, JavaScript, Python... The documentations should nevertheless resemble eachother and be consistent. This is additionally made possible thanks to the the divio documentation standard, as seen in [(2) all-encompassing](#2-all-encompassing). - End-users should not have to jump through hoops to read the documentation. If you have a local clone of a repository, you should not be required to have an active internet connection to read its documentation. ### 5. Micro-first -Semantic.works is about microservices, so that means a bunch of documentation will be **small**. This means it is important to not *have to* adhere to the structures of big projects. A documentation folder for something that can be explained in one README is a no-go. Start from a single (README) file, and expand only if necessary. +Semantic.works is about microservices, so that means a bunch of documentation will be **small**. This means it is important to adhere to the structures of big projects involuntarily. An entire documentation folder for something that can be explained in one README is a no-go. Start from a single (README) file, and expand only if necessary. ### 6. Non-proprietary Our repositores and projects are made to work anywhere, so this should be the case for our documentation as well. Websites, services and hosts shutting down\* or changing their licenses\*\* is not unheard of. No editor lock-in, no host lock-in. -\* -\*\* +- \*[Freshcode/freshmeat](https://jeffcovey.net/2014/06/19/freshmeat-net-1997-2014/), [Microsoft CodePlex](https://devblogs.microsoft.com/bharry/shutting-down-codeplex/), [Google Code](https://code.google.com/archive/) +- \*\*[Terraform/Hashicorp products](https://blog.gruntwork.io/the-future-of-terraform-must-be-open-ab0b9ba65bca), [Red Hat Enterprise Linux](https://thenewstack.io/how-red-hats-license-change-is-reinvigorating-enterprise-linux-distros/) ## Implementation -To achieve all of the above goals, we have the following things in place +To achieve all of the above goals, we are putting the following structure(s) in place: ### Write the documentation All documentation ends up in one of the following places @@ -45,30 +45,29 @@ All documentation ends up in one of the following places - If it's something useful for people getting started with the semantic works stack as a whole, it should go in the `mu-project` repository. - If it's about a specific microservice or project, it should go inside of that projects repository -The documentation should ideally be written as presented in [project/how-tos/documentation-quickstart](../how-tos/documentation-quickstart.md). This also covers how to tag a new revision +The documentation should ideally be written as presented in [project/how-tos/documentation-quickstart](../how-tos/documentation-quickstart.md). This also covers how to tag new revisions. -For microservice developers and most people working on semantic.works, this step is already where it ends! This will ensure that most design principles are met. However, to have a more readily available & traditionally structured documentation, the following steps get executed. +For microservice developers and most people working on semantic.works, this is all you have to do! This will ensure that most design principles are met. However, to have a more readily available & traditionally structured documentation, the following steps get executed. ### Semantic.works [View the fork on GitHub here](https://github.com/Denperidge-Redpencil/semantic.works) -Semantic.works was (re-)developed to contain all necessary links & information about our repositories. However, we ensured that none of this would *not* be hardcoded into the site, thanks to... +Semantic.works was (re-)developed to contain all necessary links & information about our repositories. This includes links & documentations to current & previous revisions. However, we ensured that *none* of this would have to be hardcoded into the site, thanks to... ### App-mu-info [View the fork on GitHub here](https://github.com/Denperidge-Redpencil/app-mu-info-rework) -Which is a service that holds a linked-data database and an endpoint about repositories and their revisions. This includes links to the repos, images, as well as the contents of the documentation, both parsed and unparsed. And this service fetches its data dynamically thanks to... +Which is a service that holds a linked-data database and an endpoint about repositories and their revisions. This includes links to the repos and images, as well as the contents of the documentation, both parsed and unparsed. And this service fetches its data dynamically thanks to... ### Repo-harvester [View the repo on GitHub here](https://github.com/mu-semtech/repo-harvester) -Which is a microservice that - after some basic configuration - will collect & clone all repositories and images from a specified accounts & hosts. It collect all the information app-mu-info's data model needs, including having all documentation aggregated & parsed into the divio sections. The documentation stuff is thanks to... +Which is a microservice that - after some basic configuration - will collect & clone all repositories and images from specified accounts & hosts. It collects all the information app-mu-info's data model needs, including all documentation aggregated & parsed into the divio sections. The documentation stuff is thanks to... ### Divio-docs-parser [View the repo on GitHub here](https://github.com/Denperidge-Redpencil/divio-docs-parser) -Which is a Python package that, when given a directory, will handle everything in terms of finding & parsing documents. It returns a class filled with easily useable and fully parsed Python dictionaries. +Which is a Python package that, when given a directory, will handle everything in terms of finding & parsing documentationo. It returns a class filled with fully parsed & easily useable Python dictionaries. - -*This document has been loosely adapted from Denperidge's learning writeup. You can view it [here](https://github.com/Denperidge-Redpencil/Learning.md/blob/main/Notes/docs.md)* +*This document has been loosely adapted from Denperidge's learning writeup. You can view it [here](https://github.com/Denperidge-Redpencil/Learning.md/blob/main/Notes/docs.md)*. From 2079fcef1724540b7ee95dc5e084ff423f2b7370 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 9 Oct 2023 15:42:07 +0200 Subject: [PATCH 36/52] Imported masterclass 01 - How and why pt1-2 --- README.md | 7 +- TODO.md | 1 + assets/README.md | 1 + assets/logged-in-user.svg | 149 ++++++++++++++++++ assets/shared-data-models.excalidraw.svg | 17 ++ ...imple-mental-model-advanced.excalidraw.svg | 17 ++ assets/simple-mental-model.excalidraw.svg | 17 ++ docs/discussions/masterclass.md | 82 ++++++++++ 8 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 TODO.md create mode 100644 assets/README.md create mode 100644 assets/logged-in-user.svg create mode 100644 assets/shared-data-models.excalidraw.svg create mode 100644 assets/simple-mental-model-advanced.excalidraw.svg create mode 100644 assets/simple-mental-model.excalidraw.svg create mode 100644 docs/discussions/masterclass.md diff --git a/README.md b/README.md index 702996b..1c53958 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -This repository is used to track issues that apply cross-services in the full semantic.works stack. +# Semantic.works (mu-semtech) +This repository is used to store information and track issues that apply cross-services in the full semantic.works stack. + +If you do not know where to get started, check out any/all of the following +- + ## Reference For technical information in semantic.works, you can see the following references: diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..95d49fa --- /dev/null +++ b/TODO.md @@ -0,0 +1 @@ +- [ ] Expand semantic model example as requested by Aad \ No newline at end of file diff --git a/assets/README.md b/assets/README.md new file mode 100644 index 0000000..bc9d160 --- /dev/null +++ b/assets/README.md @@ -0,0 +1 @@ +Some of these files end with `excalidraw.svg`. This means that they were generated - and can be edited with - excalidraw. \ No newline at end of file diff --git a/assets/logged-in-user.svg b/assets/logged-in-user.svg new file mode 100644 index 0000000..e99c6e1 --- /dev/null +++ b/assets/logged-in-user.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + "Aad Versteden" + + + + app:users/1337 + + + + app:accounts/1337 + + mu:session/account + + + + SID:USSESION:example.mu.semte.ch/ae86-gt86... + + foaf:account + foaf:accountName + mu:password + foaf:name + + + + + "madnificent" + + + + "sekrit" + + + + + + + "not-a-miata.png" + + + + "1c0f-fee4-1337-11..." + + + + "image/png" + + + + + file:///var/www/z4.png + + + + + + + + app:documents/E85 + + nfo:fileUrl + dc:format + nfo:fileName + dc:identifier + + \ No newline at end of file diff --git a/assets/shared-data-models.excalidraw.svg b/assets/shared-data-models.excalidraw.svg new file mode 100644 index 0000000..8cf3fe2 --- /dev/null +++ b/assets/shared-data-models.excalidraw.svg @@ -0,0 +1,17 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO2YW0/jOFx1MDAxNMff+Vx1MDAxNFXndenE93jeXHUwMDA2yiwsMMN1uaxWKKRu422ahCS9MeK770mgcdpcdTAwMTRoXHUwMDE4diVcdTAwMDSpVCknvlx1MDAxY1x1MDAxZv9/PrZ/rjVcdTAwMWHNdFx1MDAxYanml0ZTTVxcx9ed2Fx1MDAxOTd/y+wjXHUwMDE1JzpcZuBcdTAwMTPO35NwXHUwMDE4u3lJL02j5Mvnz6ZGy1xyXHUwMDA397WUr1x1MDAwNipIXHUwMDEzKPdcdTAwMTe8N1x1MDAxYT/zf/iiO1nd8cXV1kC4nueNnO5+e7SxI/lFXjUvVDjj+zpKlPkwXHUwMDAxK2O0eJ9mfiFUvI91J/XARpBV2Dyle16aXHUwMDE5LV5cdTAwMTidoOdnXZhiSVx1MDAxYYd9tVx1MDAxOfphnHX9SVlcdTAwMDRcdTAwMTFkur523H4vXHUwMDBlh0GnKJPGTpBETlxmIzXlutr3j9Opf1x1MDAxZiPH9YZxaVx1MDAwMPe9nD24iVx1MDAxNuxFvSSEiJpa0G3PXHUwMDBiVJLM1Vx0I8fVaVx1MDAxNlx1MDAwMmSZcWQ+RjudPPR/XHUwMDFir2JnoHay2Fx1MDAwN0PfLzdcdTAwMWN0XHUwMDFlXHUwMDFhnk2RiT9+sNxcdTAwMTk3lerk/WGJXHUwMDA048JcdTAwMDTU6IQgtmj9XHUwMDFlXHUwMDA2uWaITVx1MDAwNLEthotcdTAwMDI6aYNW0rzVruMnysQ7c22rpCMzmGHUce6rIC65TW3KXHUwMDE4KanC10F/caR+6PZNL2ulYS1I83Zjet2+PWT/XHUwMDA07eveyfn4+1x1MDAwZZl4VWmmapLO69JcdTAwMTb2nC6pVdUlkqxFXHUwMDEw55xxKimiJUVcdTAwMTYyxexDpY+rdO7Dg1x1MDAxY4WgNrPhb4lcdTAwMWGRqFhnakTcJtySXHUwMDEy01x1MDAxN8hxzo+KIFx1MDAxOVx1MDAwMkWiXHUwMDFhgjT6ynRcdTAwMDWxPVI9XHLhdlJwuJGoeKTd0lx1MDAwNHXDID3Wtzmk1pz1mzPQ/nRuXHUwMDA2sia/+rqXhaPpq25JXHUwMDA2XHUwMDEwkVTD8l18TsOoLKZEget5rExcdTAwMTRd6MVcdTAwMDFrXFydqDDWPVx1MDAxZDj+yUqDyJrenqlcdTAwMWW1MHuCStpccslkM944XHUwMDE42INvRyeEk/hcXK9CJVx1MDAxNyZcdTAwMTB5tmBVKiVpXHRCXGKWQmBJJTbLk4HSaOR5KJHKfu9cdTAwMWNKIFx1MDAwMGIvKFmWIyhdNM6gXHUwMDA0JGE5L9d6LSapXHUwMDA04K2y+uoyeVxuXHUwMDEyXHUwMDBlIFx1MDAxOLU4JK/KYamXXHUwMDFhXHUwMDFjVlx1MDAxZF9k71x09Fx1MDAxMteadp3e2eWPtur+sX482e2xs1XQY9KAlEeCmUmdoSesXHUwMDE2seAhXHUwMDE2g1xyhbCNRD/QezF6XHUwMDA0c5sxXFxcbn8pXHUwMDFmWo/nQyFcdTAwMDVcdTAwMTewsXvJ9uxcdTAwMTn2OIVk+yvsba1cdTAwMGZcdTAwMWPtvznyXHUwMDE23a7B3e3u5ni8vz3oXHUwMDFj+0eXe2N5c3PBg1W4XHUwMDEzdD7lwVx1MDAwZfEj5f0vKc8mllx1MDAxNETwSnbLUl4lXHUwMDBmXHUwMDE23FGYXHUwMDA2S6JScnq9nIeptMvqq8vdgZMk4zDuvDnyqo7XYG+ERe8oujw/8ta3+4dcdTAwMDdk/Xe7u9IhkCMzzfkhXHUwMDEwLzlcdTAwMDQy/Pwh8Fx1MDAwM766h0BJYKHDJbGslPQwhz0qUPL6Z0DKXHUwMDA1JrisvrrstaHBRthtXFzrOPXeXHUwMDFjgI94X4PCTW9cdTAwMGbF/b3dP6eH+uD0qnN+dTGhK95cdTAwMTJyMr/5RKVcdTAwMTPsXGZELORcdTAwMTLySsZcdTAwMTXQk1x1MDAwMrn4XHUwMDFkoVfnlpBa2Fx1MDAxNoTRZUjyinFGpIR68HAzX69GpC05qbNcdTAwMGJ9Qptq9HV9n/bl2fbNqXT44CDwtk5WyVx1MDAxMJIsXl+LaobApGVBhlx1MDAxMFx1MDAxNsbCYlbpXHUwMDBl62XXhO9OpssyXHUwMDA0zL3NQY5Lr1x0S3vkxe2ZLZgkXGJb/8U1IbF/7Vi0XHUwMDE3wpr7Zu9cdTAwMDdcdTAwMWbx/pGLQfjPNdB0oug4hUhcdTAwMTbLUXOk1XijqulP3fxprj1QnOlZ5avY3drdv1x1MDAwN97jbCJ9 + + + + + Registration serviceaccountNameE-mailpasswordbirthdayLogin service \ No newline at end of file diff --git a/assets/simple-mental-model-advanced.excalidraw.svg b/assets/simple-mental-model-advanced.excalidraw.svg new file mode 100644 index 0000000..8d01b8b --- /dev/null +++ b/assets/simple-mental-model-advanced.excalidraw.svg @@ -0,0 +1,17 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1dWVPbyFx1MDAxNn6fX0ExVfdp0O3tdPeZN1xiYVx0+5aQ3JmihC1cdTAwMWLFxja22Hxr/vs9bVx1MDAxMmu1bMBcdTAwMDZzg7NcdTAwMTTIstTqPt9yevN/f1tYWIzuO8Hin1x1MDAwYovBXcVvhtWuf7v4hzt+XHUwMDEzdHthu0VvicHvvfZ1tzI481wiijq9P//97/hcdTAwMTNepX358KmgXHUwMDE5XFxcdTAwMDatqEfn/Yd+X1j47+D/xH26QSXyW/VmMPjA4K34Vlx1MDAxMlX26G67NbgtVyClRm3t8Iywt0r3i4IqvV3zm70gfsdcdTAwMWRa3Lizq58jYVb2cOl86zjC2qq9jG9bXHUwMDBim82j6L758FB+5eK6myhUL+q2XHUwMDFiwZewXHUwMDFhXbi7Z45cdTAwMGY/12tTXHUwMDE1xJ/qtq/rXHUwMDE3raDXS32m3fErYXTvjjE2PPpQXHUwMDBify7ER+5cXFx1MDAxNSj0XHUwMDEwpFFKoNVcdTAwMWFEXFwj7lx1MDAwMkJli/Kh3Wx3XVF+XHUwMDBmmOSSx4U59yuNOpWoVVx1MDAxZJ5cdTAwMTN1/Vav43epjeLzbn88pOBieOxcIlxi61x1MDAxN5ErMUePJV+J+1x1MDAwN4Oq51xc0F+tXHUwMDEyLePu2tmsXHUwMDBlwuDvuMK7/mWw6T7Sum42k3XWqv6os5/hXHUwMDEyXHUwMDA3jPxx5J/4sdz5XHUwMDFms4GWXGa2VMBFwV38rInowKhxsH59fNfb//xlr1x1MDAxMuDW5YfgcHF43j9/XHUwMDE0X/bhw2xHhWyrvb6+Ybebn/ZXOpVK0E7f5ef9/W63fZu47o+f4mq57lT9hyDmLr6pxZnmUlx1MDAwZt9vhq1Gts6a7UojjvvfXHUwMDEyXHUwMDA1XHUwMDFlXHUwMDAyruwph9cprKSHODTKXHUwMDAzLZhRzHDNldLpONTgQS6CuFx1MDAxNp6xUiqmQHHGQedcdTAwMDPKlCFgWiH92th+Mlx1MDAwMFJv/Fx1MDAwNFx1MDAxODCwXGZcdTAwMTXGtVx1MDAxOVOmZtmDQ8bUXFxI1GBjXHUwMDA2XHUwMDE5zZhZUKXK8fxcYo1cdTAwMDPOXHUwMDA1XHUwMDFh1e1R6Np+oePXg4W/Wn6nk2i7dis6XG777lx1MDAxMYRNXHUwMDFkXfMvw+Z9qvrd9ZabYd1VxWKFXG5cdTAwMWZ0U8JcdTAwMTKFJFLDXHUwMDEzLsNqNak8534voMJcdTAwMDfpeqzQrXw62t2cREba3bBcdTAwMWW2/OZxwZOlnsvdaGNIq56AQthOpJNKmlGtblx1MDAxNGjgNlx1MDAxMYvjZHJcdTAwMTfXu1x1MDAxMavJjeqx3Kx07f7aTVx1MDAxNedcdTAwMDdKxTKpJfckXHUwMDAwckQ0ysZcdTAwMTdcdTAwMTmQkzSeQMakXHUwMDE2mmulXHUwMDExMuV6dc00Vlx1MDAwYmH0XHUwMDE0XGLjXHUwMDA1XHUwMDE1s1x1MDAxMq7ytb3ubX8l4H63cX7YXHUwMDBmjz69gGKWXvf86mOr/6m9/fX47Duu9zeuXHUwMDBlard386jExbU3gVx1MDAxMmsjPSZRgJLKMm3SoW5wVKhcdTAwMGZFWVhPS3pxcpRW2KKIlVCCt3dRLlx1MDAxMmWrkSnkpkiUS9JcdTAwMTiQgqGh/+ZQlDerdKOwXHUwMDE2JjX0ddRYxPGYUeMxapVV46JHmqZcdTAwMTArXHUwMDE41dKW8IpcXCZaZJxcdTAwMTArPLtdbpyf6F6A31aPri4+X3XM/MCnWIjdXHUwMDEzelxurcNcdTAwMDOBXHUwMDAxZCZPeCBcdTAwMWZKXHUwMDA3XHUwMDA0XHUwMDAzZdFkXG726kosODPGXHUwMDAwiOLsNVZXJT6sNL/511dfP13ss5VDddv7fvNTTV5Lslx1MDAxZlx1MDAwMues3d1oVbvtu2NcdTAwMTVGh/tHX6o3+7UpaOv6zeXNl/vdtU2xebW/wXfPv271p6HZ91fy9Pbw48eNlf1TvrWn71t7zZspXFx35dOHq9bW57PvkamHWsEt9O33efRcdTAwMDJwtH1XNe3Tbu+uwXy+ub19Yy4m8Vx1MDAwMlxcXHUwMDFh8FxiJlpxSu64kjKNNiU8a1x1MDAxOGfWWIWUuVx1MDAxN7lcdTAwMDH0pC13XHUwMDAz4ldzXHUwMDAzs0B5kWmQlKswhlwil7O7sqHIXHUwMDFl/SklgikrmLR6LjP5qN1cclx1MDAxNv61cHTfqlx1MDAxNNtcdTAwMDaWOjrONjSDWlRiXHUwMDFhonan0DHwmL/TjiH1LLlkvbDwk1x1MDAxOYRcdTAwMDco1/tXSyeNw5XvsHZ+3ZD9fuh37GRQVrZMOJGnhDPh+4dQZsZDrjWRgZAggVx1MDAxN/S2PVxuyjxwf96hvDCJ/1dGgaGaL8SyXHUwMDFliWXiZUKzNfFcdPNcdTAwMDPl427YaVx1MDAwNj2HiTeH5MKyP1x1MDAwNsj26KRz+enGXFy2P5zK5YOjm898eXlcIiArKFx1MDAwNbKx44BMms5cYsZcdTAwMWE0iTandszjXHUwMDE4jFx1MDAwN5h8xVLwjutp4tpIXHUwMDAwxrTMXHUwMDAxeFx1MDAwMLTRne1WM665tfOY1++1XCJq5/r9Xy0qUeT/1TpcbirXXVffRShPyEhcbuUydd3J0/xROIc4hFx1MDAxZoHzXHSeJYv6QtBPlt5rXHUwMDFj1eCohUBcdTAwMDCchMdcdTAwMWZcYuZELNf7q/7W953ube3uTJxFvt2cXHUwMDFmsFx1MDAxNaf3yFxc/6LWjtfAXHUwMDEyh6W5TSrPMlx1MDAwNM5cdTAwMTllXHUwMDEyxiiVKdirp/d0XHUwMDA2s6BNop3eQld79eNu69Oaf8ZcdTAwMGXPztnB3t79l9VITdwl3rltbVx1MDAxZcJH2W1e9PdXP1Z2vu1M2CVeel11tbHfOunh8T7u9pubrHZ+fbwxhetGXHUwMDE3X1rnn1x1MDAwZva/7m2cXHUwMDA2rFpbP2tcXK1O4bp3a1xu75e7O9jo93tyaXn/ZHv35E1cZjnEXHUwMDE24YndXGbFbiWhZKNcdTAwMDdcdTAwMTAtouDAXHUwMDFmQWzFwTrvxKa4h4qIzVKFXHUwMDFiKTMjiEa/XHUwMDE0sXGlPe1cdTAwMWOdRsrylShcdTAwMWad+dlrqaXhiUJNZ2gjR17TjNPChGKiXHUwMDExXHUwMDA1V4WrIVVgVLlIOo3njTZkxlx1MDAxNcaoc9aGXHUwMDE0lSeXbVx1MDAxNLme54w2QFwiNcr6T4pjpZk0k1x1MDAwZjdcdTAwMWM09mpfXHUwMDFiXHUwMDA3347vXHUwMDBmb9Z2w5N1JVx1MDAwZbfnXHUwMDFktpxcdTAwMGLwXGKPhEmjJUJ6NJSgZKVlXFxcdTAwMTFydVx1MDAwMklPwOzvtZo11kzPiFxiNEKLZH/BWzBcIvfd7sXJgert6Cg83e2s3KzvLy+9tlx1MDAxMXnGuMRcdTAwMDRcdTAwMDJMlC9VXHUwMDEy1LNcdTAwMTBgwUamklxulYDUbK9xSC5upLlHsrKeRCxGsrVTQ3K5+mrtJUZJSiSXXHUwMDBiSoGUsmxcbtNcXN+K6lx1MDAxZVx1MDAwNvWQatyPXFzQzkZ3x6hQVneLSzR75cWR4/xcXDKFRmo2+cR0XGZ3XHUwMDFh6lhcdTAwMWVcdTAwMWT1Vlx1MDAxYTvdqlxmPsq+P/d4XHUwMDA1zT1mNFh6WmbIcJRJb6JcdTAwMGZzXHUwMDFlxFx1MDAxNyx305hcdTAwMTPzkd+C+Na/Wmhgey1cdTAwMTJb+7dwsLO+02l8f+1s/Vx1MDAxOZNcciZcdTAwMTFfaVx1MDAxMpM5ZyW+Vo5Cs1JgNFx1MDAxYT05mItcdTAwMWJp3sGspfIko1SfMl8rtUrPI8io7/PAXFyqv25cdTAwMTmBm5soKPU1zDCZh3deiq1gRqDFKaD5rSjxdptkcEZcdTAwMTI8Ro6yXHUwMDEynCnK7LVXj0QrV1x1MDAxNFx0zCo1+Wz3XHUwMDFh1ndh+/xT/aCmXHUwMDFhd+xcdTAwMGL3r9aO51x1MDAxZa7ZrDc9xKi49lx1MDAwMI2RrjqUMXa+xFdrZFx1MDAxYdU0ZuK+ZFx1MDAxN3zv8q62flirbW3J61O0IVvV7LW7tJ8xc25cdTAwMTLxNVxuMFx07JmIL4y20ii1RWH4I/qeXHUwMDBiW2nu4aylx8iUKq00cJFILlx1MDAwNnBcdTAwMDY7RTiXp7/SeOCmXHUwMDEwXHS6IdljMVEqTKdZt9zg15Hfw+Bh/XNvRlx1MDAxMjxGlfJZcK44M5dhrUfjXHUwMDE2XHUwMDE0eWZj5eS4ve1947X7PqxXt28/nK7VVm+/1Hpzj1t6Tk9ITcgkIZYsXHUwMDAz27lWYbCaMeQjprnPq1xir5xfqqhfjzrtI99cXPDNzZPTm9Zrj//Odpq5JFVgSVjPQoTl6Fx0ipwhXHUwMDA1JzxiIKm4keZcdTAwMWTLlNd6XHUwMDFjgWlrSIaTqHphXHLWzGNuQr5wXHUwMDAz0dqAzcO7oDfaTWmhlvqFNHgtbM5Mf8fIUVZ/M0V5jvZOMlKWoZN0XHUwMDFjs4HsaGEpXHUwMDFhUFx1MDAxOczsXHUwMDE3XCLI4Ek6R1x1MDAxYWmsXHUwMDEwiaGUeFx1MDAxYbn0SGqQeMdIa5Nd3bG+MOtZXHUwMDAxmlx0RJ6YXHUwMDE0N5XlISR1XHUwMDE1rMzRkvMpKqPIKeOwXHUwMDBmXHUwMDBiODdC5FaKOq1cdTAwMWXptMhcdTAwMTFcdTAwMThcdTAwMDGzWjz6JCx32mFW4uOfXHUwMDE24shcdTAwMTj8Mvz57z9cbs+mdIQhumC1ksI2XHUwMDE2IfdassKzXHUwMDAwXHUwMDE2tFx1MDAxNVJwKvW4y41cdTAwMGXtwfUyUVx1MDAxZF8tJ9NNv1x1MDAxN31oX16GXHUwMDEx1dq+e+Js7fRcIr9cdTAwMWKthK1q2KqnI+HHnkiTTDNcdTAwMTmQYOXa1eZcdTAwMTLzXHUwMDA0XHUwMDA3LZlcdTAwMDS3uJuqXCJRXHUwMDE3i3W/46LBXHUwMDEzXHUwMDA2yYtcbqSzwNVILtqCVnV8ocrH4Fx1MDAxMoVinnQ9OdqSapJHUFx1MDAxMK9IXHUwMDFhXHUwMDE2XG481zXLhdu1hGqV5cs0qKplR2VcdTAwMTeBn8NcdTAwMTOVOPle0kJcdTAwMTVzZ/lAR1x1MDAxOXei5Vx1MDAxZWpEXHUwMDEwhitcdTAwMGJcdTAwMTk/L1x1MDAwNHiMYo1cdTAwMWWTQkiwXHUwMDAy6nRDYkpcdTAwMTGOkVx1MDAxOWohVcCcXHUwMDA0c48pZo2jaEjaqnfufCp3XG5yqGR/XG65s2TpXHKXgprBiolmVjyOPoFxSFxmiL5cdTAwMTZ9XHUwMDEy3DyjgSHQo2opeZrvcrE47noj43twOS6Uh8xt/kX6XHUwMDAxblx1MDAxOGfeXHUwMDE4XHUwMDE0JJXPdWqCUsisSJz1QFboXHUwMDE5XHUwMDEwbt2NQlx1MDAwMUZwnVx1MDAwYrmJXGK0fFx1MDAwNCVdJkk3XHUwMDE0rrLodlZcdTAwMTSUiVx1MDAxYk9rRzqowE0/VWrWXGZa3ls9xn0qj1x1MDAwYoFuPlx1MDAwMFx1MDAwM2PSWZSkLEpzXHUwMDAwhZwsKmlHPJLzk0KRnlx1MDAxNsFQjJHCkeLH3jJhPsnDWovKKrqVfHefU2BQwq7UULh4UY6cZEpOiYhusr2l3qb9ZG5BrSQ6sFpcdTAwMTiAeLtcbvcyzFOWpMeB0lx1MDAxMJuocZdcdTAwMWJcdTAwMTnb7pWN6rniTjKfrkzWuo5Ow4ROKslcdTAwMGaeckt/NJNkXHUwMDA03d6cXHUwMDFj88E2XHUwMDExd5Z3fae5Uzm761beXHUwMDFh7lxme1x1MDAwMXdKXHUwMDBmXHUwMDFk3SjBtFFI/nnW3FneyVjqPlxyelxutVbk3YHCID1cdTAwMDFSatImRbrFpCHDjTa/lVx1MDAwM8HRXHUwMDEzTFx1MDAxMXqIX4nninZyUNKj5IFcdTAwMWMuXHUwMDA3Slx1MDAwZpJLXHUwMDA23snzqeTJhUtcdTAwMWEsVWpcdTAwMDF7UoOM9J9WuoV0elx1MDAwNv5Tka2Fx0xanpH/JIrUYMl4olZuJrVMfjxcdTAwMWaM4/3niFxiXHUwMDFm3Cyb3M9cdTAwMTmDasaIQiWCWz4pWT595+ChXHUwMDA0pGxmMFxurPjTXGK0vO8yVSRF5SFQc7LyXHUwMDAyScZyRdJkmVx1MDAwMVx1MDAwNuVliv7lizRl+iyfTVhqPTVcdCjZTkJcdTAwMDBS3On0dFQ67lk7yFrIbVx1MDAwYp3P3Vx1MDAxMZzGKadtZFutLuh2V9pDJK1XjOSGQixm6HfyfDJ5UqxcdTAwMDMyXZi789EzvTi1s5VsVuvrX997Lo1cZkj3yoXiS5Ddo9Ja4i+k3Fx1MDAwMI0g5k843SGzMCW0m9zDkJEyPDHTLt8jL1MkzshVXHRtKNtcdTAwMDaLXFzmXG7l0n+0rkddkp2npHvWdFe+cqmU7lx1MDAwNFJpkXFDibQ0WqY7K8fznUW3i+hgXHUwMDE5XHUwMDFhU0ZCTFx1MDAwNjHfWc9taiBISy24dcDvfPf8TJtZsvBK59d7LbrNRkbyXHUwMDFk5dmSJFjNaMuv1+e7kfHoXrlIfFx0unvEMIigzMwgMrcxm+bCJvpcdIr5riBcdTAwMTGdLt9cdTAwMTHdke3W4DbrXHUwMDEwlLfbfMJuPXDjZlx1MDAwNpWSVOGI+ZidLtuVz1YtZTvJ0NOWkn1NlSjJ5qXYTnFOXFzo4pn4jrnO5zzdXHUwMDAxSVx1MDAxMp1CXHUwMDAx5Fx1MDAxNt2qgj2VKLlcdTAwMDB3XHUwMDA154qJXHUwMDE22LS3Pfwl+Vx1MDAwZVxcf4ZM7oOU9Hejt0XjYFxm4zDJXGbCt0l3o+LRvZbyofhcdTAwMTKE95iuN7dBmZBumSNYrXRcdTAwMDG9pPtcdTAwMDPlSzg8XCIzyv7oXpZy1oIuSum+woRrN4GLpNTYmfu78qmB5eksZ1x1MDAxZSepcGNtgIZl5qNcdTAwMTFcdTAwMWa6lVqDPea4XHUwMDEwXHUwMDA1jJfJXHUwMDFmXG5cdTAwMDZSgHtujpBb9KVRkfK+XHUwMDBmRk8hoSXguu6pPLc5iVx1MDAxOT1n2lhmXdfu/63Byya0qdGPpXwsvlx1MDAwNOM9oq/MTZhcdTAwMWKMXHUwMDFkc04uXFzlXHUwMDFkXnr2XHKZ9Zk7PFx1MDAwMSQhxo1Wc5Aj8tnkkIGc+dyb8m/1KOM70NIjXGag+95cbpBkW1N0R97fXHUwMDAzpd2KbXJ4omDwwy1cdTAwMWNNXHUwMDE47KKFK9zN1Y9H0lx1MDAwMd7776ZAd0QxVJtcdTAwMDVbYTq9Lem/s0KY1JjJ/1x1MDAxOd2NXGZI91rKx+JL0F35V1dlXGaecns8XCI5POFcdTAwMTZsYt5N0TNcYjfLkNJMbVxy/X3idMPyr/BIMVx1MDAxZTNUa6QhqKTrXG5g+UJcdE9LqnalnSuSVlx1MDAxNlx1MDAwNO50Ka98978yyrNKelx1MDAxNtHN1Fx1MDAwNJbqgXugPJdcdTAwMDMgKCeVViQ6foaUxz16Pve2If/HWcGIxZS/W+/XZDhBTWO4LpxeKEdcdTAwMWE6cuhSaTnRvolvk+BGhV/m07Ojs4mJg+iMM8WU4ypycIJcdJM3S09cdTAwMWNufcRcYjCjjFx1MDAxOdxcdTAwMGVcdTAwMWJkJZ1cdTAwMGJm+dnSLkOlpFx1MDAxZlx1MDAxNFx1MDAxN6DdXHUwMDE2fvms+en89duPVlj0O52jiIJq+FiLN2Fwu1JcYnj3cp9cdTAwMWZcdTAwMTTAXHUwMDAxPlx1MDAxOODnn9/++Vx1MDAxZlx1MDAxNC9cdTAwMDdcIiJ9 + + + + + Single page appIdentifierStore & SyncTriplestoreOntologyDeltaSecurityDispatcherRegistrationLoginResourcesFiles \ No newline at end of file diff --git a/assets/simple-mental-model.excalidraw.svg b/assets/simple-mental-model.excalidraw.svg new file mode 100644 index 0000000..ad36134 --- /dev/null +++ b/assets/simple-mental-model.excalidraw.svg @@ -0,0 +1,17 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO2ca1PiSFx1MDAxN4C/z6+g3Kr305jt+2W/oXhcdTAwMWRcdTAwMWNcdTAwMTlBZNzdsmKIXHUwMDEygSQmXHUwMDAxxS3/+9uJklx1MDAwNFximDAyhTMwU1x1MDAxNnS6Oyfd5znn9Okk/30qlbaCkWtu/VXaMlx1MDAxZlxyvWe1Pf1h63NYPjQ933JsdVxiRb99Z+BcdTAwMTlRzU5cdTAwMTC4/l9//pm00Fxmp//SyuyZfdNcdTAwMGV8Ve9v9btU+i/6q45Y7bAtcdyz1nHLPfs+OMNPYu9cbpVPqlHTqNJYmMB8XGaS0kdVRFx1MDAxMIx/j0KhKIl/P1jtoKPKIEVcdTAwMWEhUkomiUBcdTAwMDShuEbHtG47QdQsLtPt2154Mlx1MDAxMJf4ged0zV2n53ihXHUwMDEwf5hcdTAwMDBDXGZcdTAwMTM5rnWje+s5XHUwMDAzu1x1MDAxZNdcdDzd9l3dU9ec1Luxer16MOq9jJZudFx1MDAwNp65NXWWi7HMU+VxO99RY5u0Uqe97dim70+0cVxc3bCCcDwgSK4jlNE9akeT8G9cIpWn982jcFx1MDAxNuxBr5fu2G6/djxxwDfNsDJcdTAwMDacIVx1MDAwMEUy3IlmyJnCr45cdTAwMWQpXHTEUkiEMFx1MDAxMnFccsuvKO1cYqJOb/SebybjXHUwMDFhirCXaM6EXHUwMDE4XHUwMDAzt62/NIJMMkGBlFx1MDAwNDBcdTAwMWNcdTAwMWbvWXZ3uk3PMbpcdTAwMTnnibRKXHJt3Vxup75U02/NUtl1U1x1MDAxM+fYQd16XG4vXHUwMDAwgYnSfb1v9UZcdTAwMTNjXHUwMDFm9lbuWbfhQGz1zJuUXHUwMDAyqMFcYixcdTAwMDVHfDhw3LRcdTAwMWH5ppI6XHUwMDFhpmR4XGZ1XHUwMDE2XZV6s1PkeNatZeu9xlvyh71cdTAwMWWOVVx1MDAxZGqIRlx1MDAwN54/Z5F4QXGnzmrCbF+cn1x1MDAwZeDxY/fMOJgl0TON4IWUXHRcdTAwMWNcdTAwMTlJ4IpwJMnAjHFEMFx1MDAwM0BcYqVcdTAwMDbSn6TdXHUwMDA2yHHHMZDjOUsmXHUwMDA0v5Y8T3MqXHTGXGZcdTAwMTCQVsKxXHUwMDAxp2i6dMwplYRKXGZYXHUwMDAycn5M/46FS8SM1at1OWw2zlx1MDAxZH5zdffQXGYu9kb1WudLPHpcdTAwMTMqpnue87BcdTAwMTVcdTAwMWZ5/ryo3yt+YcHa1cm9u91cdTAwMWVcXJ98r4PWXHUwMDEzy9fv67dkXHUwMDAypk1cdTAwMGJcdTAwMDSEMS7SOL1hWlx1MDAxNvDVZGa9Su/279nll3vGXHUwMDBlnJs9hPN4urRcdTAwMDRzPVx1MDAxZFx1MDAxNlx1MDAxYVx1MDAxMVx1MDAxOGNOXHUwMDAwwlx1MDAwNHM6S9rG1Vx1MDAxNXV1kHAqmFx1MDAwNJBlQFx1MDAwNImYXHUwMDA3keSYUcyWQGjlnm7guo5cdTAwMTeUqvrI9D6en8uWPp+XW+DAkknFXHUwMDE0zptUjCWRmMA80/pC/OGjqDRcdTAwMDPEd07l9vWXRiBvKqK/PqjMXHUwMDFhgdDaYDnfkb+XkVg2IFx1MDAxOGNcdJH6z4hIlGilXHUwMDBl9Wf5uVx1MDAxY/5cYlx1MDAxMFx1MDAwNPiP+qN8JOC55lxyci4xJjJcdTAwMTVFvIVcdTAwMDKRV1x1MDAwZuXu9TnzTXlZqd93mvcuX3dcdTAwMTQgpHDdWUBcdTAwMTBwzilF68hCnepO0G18PfGPTs7w4X21xrujd2OBqlx1MDAwYkc/h1x1MDAwNTpcdTAwMTNFj1lcdTAwMTBcdTAwMDRcdTAwMTBcdTAwMWVcbpRcdTAwMWKFr/LAXHUwMDBiwFxyPmw38JHhidr+sC3XXHUwMDFkXHUwMDA1Idm6k8BcdTAwMDVDiLOftMj61Th4XHUwMDExtL/Tsu/OYTnoiGbryLtET7T8Pc9cdTAwMWFFklx1MDAxY9k4xLTNsuQ9liVSQEShXHUwMDEwWWt7gadcdTAwMGJjt1xymFx1MDAwMJCjVEpgfVx1MDAxNiYnluE5vulcci3D9D/cwmSO9EXSb7RefWxzp+X5j12gw6Nqdcg7edCDXGJMReyZ7Elccof5XHUwMDAx9UVcYiSyTOxcdTAwMDbEoiBiyVx0XHUwMDAwXHUwMDEyZXI4N8WGXHUwMDEwpVx1MDAwMkqYJ3r+6fmBwPHM0v9K9ZFtfDhcbrOFL1x1MDAwMuHdt+OnwYBefT1Eg73q0f3xfbNxnWs3ik3GR1x1MDAxODJtlkLKNCTDXHUwMDBmwlx1MDAxYzBAM7yhXG5zXHUwMDEyvXhcdTAwMWJDaIb/fnNcZlx1MDAxOVx1MDAxN1xcoTib1Fx1MDAwZaNCPl1cdTAwMTi7Q1wiIYeS51xu3Vx1MDAwYnLIlU1maVxyLMrhbs+anK+EwFRcdTAwMDJqgkA80clcdTAwMGZcdTAwMTOYnKVcdTAwMDCB02JPs7dcdTAwMDC9b/buQ9V1q+VcdTAwMWVcdTAwMWLUjreblap9mC89rqiCWFxyPJNcdTAwMDAhXGKTXHRcdTAwMWaDiJEkSkXUokFpyVxmlFx1MDAwMmZFpapcdTAwMTlcdTAwMTOCYiBcYlx1MDAwMlx1MDAxMGKaNNxQOe54IZVcdTAwMTJTolx1MDAwNm82S1x1MDAxZYUj8zeKIcOUUsjePUpV8S+XkKSVsyiWR211XCLrxpqTO4dYk4BDIFx1MDAwMaYqLiCpXbRVopp0V1x1MDAwMNWsSymAK66fVFx1MDAxMGly48i1vlxmr7f9k1x1MDAwM/MkXHUwMDBmrpyhXHKua4grYUhcdTAwMDWyKHNNXHTp3EUlXHUwMDEzmEtB39+JvtBK07pZlNaKpaYsMDq/XHUwMDAwrVmXUoBWXHUwMDA3KL35hs9deXh5vnNZXHUwMDFlom24kyuvXHUwMDAz1CAhXHRcdTAwMTlHKmJlIFx1MDAxOaJopSm4xmTqk1xcW4yrzICVajDdKrW/vYF13PFcdTAwMWJcdTAwMWLTTFx1MDAxMspQ9sb0fOcqoDKrXGauJa3pXHUwMDFjSjavSGPpTyqyWDtesy+mXHUwMDAwsbdP99vn3bOdO7p/PejipydLd0WudFx1MDAxMExlXHUwMDAwouFcdTAwMDBcdTAwMTnpIMA1XHUwMDA1tVx1MDAxYUOIMMU0pUfLpYM2VEZUckK5XHUwMDFh0ixcdTAwMWbK5uaDhEBYMlx1MDAwNFaQlv3hdWjDs9ye6YeJlVx1MDAwZpdcdTAwMGXKlL1INkjUz93+8ZD3nd1cdTAwMTYuf6tcdTAwMGabsFxczpeS5ZNcZipcdTAwMGY6wyCnXHUwMDFhXGLNXHUwMDE4ZURcdTAwMTJIQFx1MDAwNoKUazTtKFO3126QXHUwMDFjd7xcdTAwMTBJrtaSyuPhrNRcdTAwMTCbu6krlWuVdD0zQ6d2oKbvdvSPrSRcbvR/7LppXGa8cFx1MDAxODN9ZrFkkaEuXCJcdTAwMWRi5iY0teVQgNBcdTAwMWPXkstlZpOYXr/IeVNccjGkXHUwMDAwYUVhjrl+MVx1MDAwYpduu3E3ko1cdTAwMDFcdTAwMTnt48puu1aDkq1cdTAwMGY/2Vx1MDAxYviLXHUwMDAzeVx1MDAxY8ZcdTAwMDPvbkmWXFxcdTAwMDOM80eQXHUwMDAxrNaj73Bjy8SBQjv3q727eOmY+Yej4vkx6tL++t2j4+xofElcdTAwMDNAp0vj2zo5J5ySPPe7v6hcdTAwMDQ0rqtccnxcdTAwMDLN0aBROf9mu62Li8ZcdTAwMDfHXHUwMDFmydRu1XrgzyVcdTAwMTZcYvLUXdQrwv8tQJfy1lx1MDAxYkBcdTAwMGJcdTAwMDLK51x1MDAwMlxuXHUwMDAxoTRKTuZGtMxcdTAwMWFcdTAwMGZcdTAwMGaNu/6d22fn9rBb7dbuxbojqmpQjSyTa1tcdTAwMGZgmeBAMMw3wP5cdTAwMTbAsrnAXCLAgYrdUH6X6l1cdTAwMGZcclx1MDAwMfdcdTAwMGYqT6hcdTAwMGYh/mp877SqXHUwMDFmnNd1XGapYfg8IGJpU7phND766zE6P+pV/lx1MDAxNFxigHNtsr8mw1rN7i46deq9yo5+urf7dHpZMT86o2tcdTAwMTj3XHUwMDEyqfCEdONGf1x0RPM8XHUwMDEzNpVBmFDgmShQilx1MDAxOT3DPEPPXHUwMDE2gPHb38o7+3xcdTAwMDd6LZl5iFx1MDAxZUpCXHShPHNLZ7YwTiBcdTAwMGLKOSXvv6VT/Fx0wLjYdazphFfyrZToR/Qj/v7v58zaic6Fn22Y1P801W6rp/vBrtPvW4G6kFooxLTAfqB7wY5lty37dnJ6Xt9cdTAwMWVzlONx2sisXHUwMDE4g/BcdTAwMDKBpsy+oICIcFx1MDAwZVx1MDAxOFx1MDAxMlgknjBUXHUwMDFj3VxyXHUwMDA3dGbOTbv9tlx1MDAxNItfkzEhXHUwMDA1VDGwhIJcdTAwMTOMkEhcdTAwMDfEXHUwMDBiZIjGolx1MDAxY1x1MDAxYYKOqc9osZIwfSydc8y2PIvzl4ssj6BMXHUwMDAznFx1MDAxMDV8Uo1hXHUwMDEySbyYIaxcdTAwMTFcbqVSRqWVqbX7XCKbVOS+5o1NWmCTXHUwMDA0YVx1MDAxNGXvaaG5N2qphTmSkq7itsrwJVx1MDAxOUmss1x1MDAxZSZcdCXVV2eRctuCbWVcZiRnQDBCXHUwMDA1o1x1MDAxY+BkXHUwMDEzMDZcdTAwMDZogrflrNPivamURFBTQjAshORcdTAwMTIhNY2ztil8d5ZcdTAwMDAylFx1MDAxNqJwXHUwMDE3ddXGavFjkouMXHUwMDE1hFx1MDAxMC2yVvRccmuVUFx1MDAxM1urRUuLjbUqYK04YVxcXGKS+VwiXCIy96ZcdTAwMTg1niEnMNc7XHUwMDA2XG7aq6JcdTAwMGbMrshe4TnVV2evXHUwMDE2P3o+aa+AIIKo8JdQXHQwZjAl7auBIO9gr1x1MDAxNr9cdTAwMTdiMqZ7UyCqSUQpXHUwMDBlb+nFfFag5a3Vp9fZ2NJdt1x1MDAxZShtiq9oa2iZXHUwMDBmO7O4/3FcdTAwMTN9wvaRXHUwMDAwIepmRM7zp+f/XHUwMDAz/mph/CJ9 + + + + + Single Page AppSupport LayerMicroservicesStore & SyncClientIdentifierDispatcherMicroserviceTriplestoreOntologyDeltaSecurityMicroserviceMicroserviceMicroserviceMicroserviceMicroservice \ No newline at end of file diff --git a/docs/discussions/masterclass.md b/docs/discussions/masterclass.md new file mode 100644 index 0000000..f48616c --- /dev/null +++ b/docs/discussions/masterclass.md @@ -0,0 +1,82 @@ +# Masterclass +In this document, you will learn the following: +1. The core values of the semantic.works stack and their benefits +2. How our design decisions reflect those values +3. What this resulted in + + +## Core values +### Keep It Simple Stupid +#### 1. Productivity through simplicity +1a. **Efficient development:** We wanted to get stuff done, without the requirement of being an expert on every topic, or creating a system that only the computer would understand +1b. **Clear overview:** Efficient development also comes in the form of overbloated applications, where you can easily lose track of what is handled where. We want to be able to look at a microservice and know immediately what it does. + +#### 2. Functionality through interoperability +2a) **Maximize freedom:** There need to be rules: as few and liberating as possible. A lack of rules would cost interoperability, which would subsequently limit your freedom in the end. +2b) **Expandability:** We also wanted orthogonal features: features that would extened eachother at no extra cost. +2c) **Longevity:** Something built years ago should still work as expected. + + +### Not getting stuck +#### 3. + +## Design decisions +### Reuse everything +- Small but focused services allow reusing everything ([1a](#1-productivity-through-simplicity)) + +### Micro +- Small but focused services allow reusing everything ([1a](#1-productivity-through-simplicity)) +- They are oftenly very easy to read, allowing a basic understanding even for those who do not know the language or framework in question +- Due to the small size of the services, debugging is easier: there are only a few hundred lines of code per service ([1a: efficient development & 1b: clear overview](#1-productivity-through-simplicity)). + +### Standard API's +- Thanks to using technologies like JSON:API, services built years prior can still work as expected. ([2c: longevity](#2-functionality-through-interoperability)) + +### Centralised communication +The microservices never talk to eachother directly. Instead they use a **shared linked-data** database. This is complemented by microservices using a semantic model. ([2a: maximize freedom & 2b: expandability](#2-functionality-through-interoperability)) + +### Semantic models +![A venndiagram of two circles. The registration service circle encapsulates E-mail, Date of birth, Username and Password. The login service circle goes over Username and Password](../../assets/shared-data-models.excalidraw.svg) +Semantic models have a bunch of advantages: +- They can easily be appended upon ([2b: expandability](#2-functionality-through-interoperability)) +- They allow re-using the same models: the same model can be used for username/password, OAuth, or even mock logins ([2a: Maximize freedom, 2b: Expandability](#2-functionality-through-interoperability)) + +## Implementations + +### Simple Mental Model +#### Basic +![A flow chart. Single Page App contains "Client" and points to Support Layer. Support layer contains "Identifier" and "Dispatcher", and points to Microservices. Microservices contains many microservices, and points to Store & Sync. This contains a Triplestore header, under which Ontology, Delta and Security are noted](../../assets/simple-mental-model.excalidraw.svg) + +#### In-depth +![A flow chart. Single Page App points to Identifier, Identifier to Dispatcher. Dispatcher points to 4 differently coloured blocks: they say Registration, Login, Resources, and Files. These all point towards a regular coloured block in between them. This block says Store & Sync, and it contains a Triplestore header, under which Ontology, Delta and Security are noted](../../assets/simple-mental-model-advanced.excalidraw.svg) +([micro](#micro)) + +- The **identifier** will create a session cookie. This doesn't identify you as a person (as this depends on what is in the database), but it gives a general hook ([centralised communication](#centralised-communication)) +- The **dispatcher** will decide which **microservice** will be called for the made request +- Then the **triplestore** will change the requested state, yield a response of what has changed in the database, read the information you requested... + +For example: this setup also means that the other services don't care what registration method was used, as they all work from the same [semantic models](#semantic-models) and database, yet care about different aspects of it. + +### Limited base technologies +- **HTTP for communication:** If you build web application, you're probably already familiar with HTTP. ([Standard api's](#standard-apis)) +- **JSON(:API) for sending data:** any programming language or framework that can read the widely known and used JSON will be able to be used. We then selected JSON:API to ensure consistency in our structure ([Standard api's](#standard-apis)) +- **SPARQL for storing data:** SPARQL queries are also just strings: any technology that can manage JSON will be able to use SPARQL in one way or another. This way we can store all our data as linked data, whilst enjoying the compatibility of JSON. + + +### Docker & Docker-Compose +Docker allows for most technologies to be used on most systems. Docker-Compose are YAML files describing the topology of Docker containers and how they interact whith eachother, which gives us a place to define a central structure of our project. ([Micro](#micro), [Standard API's](#standard-apis)) + +### Categories +These categories structure our re-usable code. + +#### Templates +These are a base for a custom built microservice with minimal overhead. From these you should be able to have a microservice going in your preferred language in minutes. + +#### Configurable services +Services built to not require (a lot) more coding to implement a (generic) feature. For example, *mu-cl-resources* is an easily configurable solution in case you need to "list" resources (e.g. a shopping cart, people in a database...). Frontends are hard to re-use, as your customer will probably want a specific feel or implementation, but universal features can be re-used to + +#### Ember add-ons +Like the configurable services, but for Ember.js frontend components and functionalities. + + +*This document has been adapted by Denperidge from Aad Versteden's masterclass 01 - How and why pt1, pt & pt3. You can view the original video [here]()* From 9d88d523986463925ca241718cf435ddb640d323 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Sat, 14 Oct 2023 17:41:14 +0200 Subject: [PATCH 37/52] Imported masterclass 02 - A shared foundation pt1 --- docs/discussions/masterclass.md | 16 ++++++++++++++-- docs/how-tos/adding-services-to-your-project.md | 9 +++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 docs/how-tos/adding-services-to-your-project.md diff --git a/docs/discussions/masterclass.md b/docs/discussions/masterclass.md index f48616c..49bd4de 100644 --- a/docs/discussions/masterclass.md +++ b/docs/discussions/masterclass.md @@ -72,8 +72,20 @@ These categories structure our re-usable code. #### Templates These are a base for a custom built microservice with minimal overhead. From these you should be able to have a microservice going in your preferred language in minutes. -#### Configurable services -Services built to not require (a lot) more coding to implement a (generic) feature. For example, *mu-cl-resources* is an easily configurable solution in case you need to "list" resources (e.g. a shopping cart, people in a database...). Frontends are hard to re-use, as your customer will probably want a specific feel or implementation, but universal features can be re-used to +#### Services +(Configurable) services built to not require (a lot) more coding to implement a (generic) feature. For example, `mu-cl-resources` is an easily configurable solution in case you need to "list" resources (e.g. a shopping cart, people in a database...). Frontends are hard to re-use as your customer will probably want a specific feel or implementation, but universal features can be re-used to fit the needs of multiple projects. + +Services in the semantic.works stack should... +- Contain hareable/re-usable/runnable functionality (see the paragraph above) +- Require minimal configuration (sensible defaults are very okay) +- Persist its state in the database +- Be user facing (usually) + +Services can be found through... +- Searching for the `mu-service` topic on GitHub +- Finding projects extending the templates (e.g. `mu-javascript-template`) +- Asking around + #### Ember add-ons Like the configurable services, but for Ember.js frontend components and functionalities. diff --git a/docs/how-tos/adding-services-to-your-project.md b/docs/how-tos/adding-services-to-your-project.md new file mode 100644 index 0000000..70582a4 --- /dev/null +++ b/docs/how-tos/adding-services-to-your-project.md @@ -0,0 +1,9 @@ +# Using services/adding services to your project + +1. Add it to your docker-compose.yml + (See the service's README) +2. Add the endpoints in the dispatcher + [View the dispatcher docs on GitHub](https://github.com/mu-semtech/mu-dispatcher) +3. Fire up the new stack + - Restart dispatcher + - Run `docker compose up` From fee309f545f22f01866c5532e8762799cd1b284d Mon Sep 17 00:00:00 2001 From: Denperidge Date: Sun, 15 Oct 2023 16:39:46 +0200 Subject: [PATCH 38/52] Imported masterclass 04 - Templates and conventions* (+) Additionally, some layout changes were done --- docs/discussions/masterclass.md | 63 ++++++++++++++++++++++---- docs/how-tos/creating-microservices.md | 27 +++++++++++ docs/how-tos/deploying-applications.md | 3 ++ 3 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 docs/how-tos/creating-microservices.md create mode 100644 docs/how-tos/deploying-applications.md diff --git a/docs/discussions/masterclass.md b/docs/discussions/masterclass.md index 49bd4de..bc2ece7 100644 --- a/docs/discussions/masterclass.md +++ b/docs/discussions/masterclass.md @@ -8,18 +8,20 @@ In this document, you will learn the following: ## Core values ### Keep It Simple Stupid #### 1. Productivity through simplicity -1a. **Efficient development:** We wanted to get stuff done, without the requirement of being an expert on every topic, or creating a system that only the computer would understand -1b. **Clear overview:** Efficient development also comes in the form of overbloated applications, where you can easily lose track of what is handled where. We want to be able to look at a microservice and know immediately what it does. +- 1a) **Efficient development:** We wanted to get stuff done, without the requirement of being an expert on every topic, or creating a system that only the computer would understand +- 1b) **Clear overview:** Efficient development also comes in the form of overbloated applications, where you can easily lose track of what is handled where. We want to be able to look at a microservice and know immediately what it does. #### 2. Functionality through interoperability -2a) **Maximize freedom:** There need to be rules: as few and liberating as possible. A lack of rules would cost interoperability, which would subsequently limit your freedom in the end. -2b) **Expandability:** We also wanted orthogonal features: features that would extened eachother at no extra cost. -2c) **Longevity:** Something built years ago should still work as expected. +- 2a) **Maximize freedom:** There need to be rules: as few and liberating as possible. A lack of rules would cost interoperability, which would subsequently limit your freedom in the end. +- 2b) **Expandability:** We also wanted orthogonal features: features that would extened eachother at no extra cost. +- 2c) **Longevity:** Something built years ago should still work as expected. ### Not getting stuck #### 3. +
+ ## Design decisions ### Reuse everything - Small but focused services allow reusing everything ([1a](#1-productivity-through-simplicity)) @@ -51,9 +53,9 @@ Semantic models have a bunch of advantages: ![A flow chart. Single Page App points to Identifier, Identifier to Dispatcher. Dispatcher points to 4 differently coloured blocks: they say Registration, Login, Resources, and Files. These all point towards a regular coloured block in between them. This block says Store & Sync, and it contains a Triplestore header, under which Ontology, Delta and Security are noted](../../assets/simple-mental-model-advanced.excalidraw.svg) ([micro](#micro)) -- The **identifier** will create a session cookie. This doesn't identify you as a person (as this depends on what is in the database), but it gives a general hook ([centralised communication](#centralised-communication)) -- The **dispatcher** will decide which **microservice** will be called for the made request -- Then the **triplestore** will change the requested state, yield a response of what has changed in the database, read the information you requested... +1. The **identifier** will create a session cookie. This doesn't identify you as a person (as this depends on what is in the database), but it gives a general hook ([centralised communication](#centralised-communication)) +2. The **dispatcher** will decide which **microservice** will be called for the made request +3. Then the **triplestore** will change the requested state, yield a response of what has changed in the database, read the information you requested... For example: this setup also means that the other services don't care what registration method was used, as they all work from the same [semantic models](#semantic-models) and database, yet care about different aspects of it. @@ -66,13 +68,56 @@ For example: this setup also means that the other services don't care what regis ### Docker & Docker-Compose Docker allows for most technologies to be used on most systems. Docker-Compose are YAML files describing the topology of Docker containers and how they interact whith eachother, which gives us a place to define a central structure of our project. ([Micro](#micro), [Standard API's](#standard-apis)) +#### Naming conventions: +To ensure quick onboarding, a lot of our docker-compose files following a naming scheme: +| Filename | Description | +| ----------------------------- | ----------- | +| `docker-compose.yml` | General **production** setup, basis for all other docker-compose-*.yml | +| `docker-compose.dev.yml` | Overrides for **development** | +| `docker-compose.override.yml` | Overrides for specific systems/deployments. Do *not* commit | +| `docker-compose.demo.yml` | (Extra) Demo to help people get started | + + +
+ ### Categories These categories structure our re-usable code. +#### Applications (`app-*`) +A full project, combining microservices into a full application. The structure is as seen above in [#Simple Mental Model](#simple-mental-model) + #### Templates These are a base for a custom built microservice with minimal overhead. From these you should be able to have a microservice going in your preferred language in minutes. -#### Services +Templates in the semantic.works stack allow... +- Minimal setup time +- Nudging (yet not enforcing) conventions +- Flexpoints: e.g. allowing template updates where new features & conventions get added with minimal effort + +There are also some guidelines to using a template most effectively: +- Ground truth in database + *(Caching is allowed, but keep in sync with the db)* +- Only talks to the database, not other microservices + *(If this happens, it's for a well thought out reason: it is the exception, not the rule)* +- Limit abstractions + *Keep the service easy to understand from a first read* +- Think about reuse + *(Split where others might reuse the service)* + + +To allow for easier development, the templates... +- Often support live reload, so you do not have to manually restart during development +- Don't require (nor recommend) cloning the template repository. Simply using the Dockerfile is enough, and allows for a cleaner codebase +- Some templates have a debugger on one of the ports + + +#### Services (`*-service` / `mu-*`) +##### Naming conventions: +- Core service: `mu-*` (e.g. *mu-dispatcher*) +- Regular service (repo): `*-service` (e.g. *acmidm-login-service*) +- Regular service (image): `org-name/*-service` (e.g. *lblod/acmidm-login-service*) +- Docker Compose name: `*` (e.g. *login*) + (Configurable) services built to not require (a lot) more coding to implement a (generic) feature. For example, `mu-cl-resources` is an easily configurable solution in case you need to "list" resources (e.g. a shopping cart, people in a database...). Frontends are hard to re-use as your customer will probably want a specific feel or implementation, but universal features can be re-used to fit the needs of multiple projects. Services in the semantic.works stack should... diff --git a/docs/how-tos/creating-microservices.md b/docs/how-tos/creating-microservices.md new file mode 100644 index 0000000..6fa126c --- /dev/null +++ b/docs/how-tos/creating-microservices.md @@ -0,0 +1,27 @@ +# Creating a microservice +Creating a microservice is simply 2 steps! + +You can find templates on [semantic.works/docs](https://semantic.works/docs) or by searching [`template` on the mu-semtech GitHub page](https://github.com/mu-semtech?q=template) + +*Note: the code examples provided use the mu-javascript-template. If you prefer using a different template, adapt accordingly* + +## Step 1: Create a Dockerfile + +```Dockerfile +# Dockerfile +FROM semtech/mu-javascript-template:1.3.5 +LABEL maintainer="yourname@example.com" +``` + +## Step 2: Create app.* + +```js +// app.js +import { app, query } from "mu"; +app.get("/count", async function(req, res) { + const resp = await query("SELECT COUNT(*) WHERE { ?s ?p ?o }"); + res.send(JSON.stringify(resp)); +}); +``` + + diff --git a/docs/how-tos/deploying-applications.md b/docs/how-tos/deploying-applications.md new file mode 100644 index 0000000..c93b551 --- /dev/null +++ b/docs/how-tos/deploying-applications.md @@ -0,0 +1,3 @@ +# Deploying applications +Location: `/data/app-*` +Server-specific docker-compose: `/data/app-*/docker-compose.override.yml` \ No newline at end of file From 5a637878d108af7e761e2b50d4ed701312093975 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Sun, 15 Oct 2023 19:17:28 +0200 Subject: [PATCH 39/52] Imported masterclass 05 - Common microservices pt1-3 --- docs/how-tos/creating-applications.md | 81 ++++++++++++++++++++++++ docs/references/commonly-used-headers.md | 5 ++ 2 files changed, 86 insertions(+) create mode 100644 docs/how-tos/creating-applications.md create mode 100644 docs/references/commonly-used-headers.md diff --git a/docs/how-tos/creating-applications.md b/docs/how-tos/creating-applications.md new file mode 100644 index 0000000..5a64a8d --- /dev/null +++ b/docs/how-tos/creating-applications.md @@ -0,0 +1,81 @@ +# Creating applications +This document will help you get a basic understanding of how to spin up your own application. + +*Note: every service with an asterisk (*) is required.* + +## Adding mu-identifier * +This is the entrypoint of your application. It... +- ... identifies the browser agent (mu-session-id header) +- ... caches user's access rights (mu-auth-allowed-groups header) + +```yml +# docker-compose.yml +services: + # ... + identifier: + image: semtech/mu-identifier:1.10.1 + ports: + - "80:80" + links: + - dispatcher:dispatcher +``` + +## Adding mu-dispatcher * +The identifier selects the right service for a request + +```yml +# docker-compose.yml +services: + # ... + dispatcher: + image: semtech/mu-dispatcher:2.0.0 + volumes: + - ./config/dispatcher:/config +``` + +```ex +# config/dispatcher/dispatcher.ex + +defmodule Dispatcher do + use Matcher + + # This can be expanded down the road to allow paths to only accept a certain type (e.g. json, html) + # These are called accept headers + # See the mu-dispatcher docs for more information + define_accept_types [ ] + + # Defines a GET path on /example, including any and all subpaths (/example/test, /example/example) + get "/example/*path", _ do + # The servicename should be replaced with the service name defined in docker-compose + forward conn, path, "http://servicename/examplepath/" + end + + # General catch-all, returning a 404 error message + match "/*_", %{ last_call: true } do + send_resp( conn, 404, "{ \"error\": { \"code\": 404, \"message\": \"Route not found. See config/dispatcher.ex\" } }" ) + end + +end +``` + +## Adding virtuoso * +It stores your data! + +```yml +services: + # ... + dispatcher: + image: redpencil/virtuoso:1.2.0-rc.1 + environment: + SPARQL_UPDATE: "true" + DEFAULT_GRAPH: "http://mu.semte.ch/application" + volumes: + - ./data/db:/data +``` + +## Adding mu-cl-resources +Our frontends speak JSON:API, whilst our backends speak SPARQL. +Mu-cl-resources allows these two to talk to eachother easily, by taking an API description and generating an implementation from it. + +For information on implementing mu-cl-resources, view the documentation on the [semantic.works website](https://semantic.works/docs/mu-cl-resources). + diff --git a/docs/references/commonly-used-headers.md b/docs/references/commonly-used-headers.md new file mode 100644 index 0000000..20d5811 --- /dev/null +++ b/docs/references/commonly-used-headers.md @@ -0,0 +1,5 @@ + +| Header name | Set in | Description | +| ---------------------- | ------------- | ----------- | +| mu-session-id | mu-identifier | | +| mu-auth-allowed-groups | mu-identifier | | From c6ae8720333efe53a268afda18c1b62c201a82c1 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Sun, 15 Oct 2023 19:21:53 +0200 Subject: [PATCH 40/52] Added extra notes from blog post to TODO.md The blog post should be available on: https://redpencil.denperidge.com/projects/masterclass-import/ --- TODO.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 95d49fa..5781750 100644 --- a/TODO.md +++ b/TODO.md @@ -1 +1,7 @@ -- [ ] Expand semantic model example as requested by Aad \ No newline at end of file +- [ ] Expand semantic model example as requested by Aad + +We should make and/or link (a) tutorial(s) for the following things: +- [ ] Docker & Docker Compose +- [ ] Linked data & SPARQL +- [ ] Ember.js +- [ ] Accept headers From 9ef02050b0236c062c63ad2d047f10c32a1c742b Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 16 Oct 2023 09:37:16 +0200 Subject: [PATCH 41/52] Deprecations & refactors Deprecations: - docker-multi-stage-builds - expirementation - microservice-reuse-and-authorization - supporting-mac-os - reactive programming whilst a great addition to our infrastructure, this isn't actively important information nor - as far as I am aware - implemented widely enough to concern everyone Some filenames have also been changed in this move Refactors: - masterclass: renamed to design-philosophy split off categories into project-categories.md - how-tos/developing-inside-containers renamed to tutorials/developing-inside-containers - documentation-quickstart renamed to quickstart-writing-documentation --- .../{masterclass.md => design-philosophy.md} | 54 +----------------- docs/discussions/project-categories.md | 57 +++++++++++++++++++ docs/how-tos/creating-templates.md | 3 + ...md => quickstart-writing-documentation.md} | 0 .../creating-templates.md} | 0 .../developing-inside-containers.md | 0 .../all-or-nothing-fails-to-innovate.md | 0 .../implementing-docker-multi-stage-builds.md | 0 .../microservice-reuse-and-authorization.md | 0 .../reactive-programming.md | 0 .../supporting-mac-os.md | 0 11 files changed, 61 insertions(+), 53 deletions(-) rename docs/discussions/{masterclass.md => design-philosophy.md} (69%) create mode 100644 docs/discussions/project-categories.md create mode 100644 docs/how-tos/creating-templates.md rename docs/how-tos/{documentation-quickstart.md => quickstart-writing-documentation.md} (100%) rename docs/{references/template-requirements.md => tutorials/creating-templates.md} (100%) rename docs/{how-tos => tutorials}/developing-inside-containers.md (100%) rename docs/discussions/experimentation.md => writeups/all-or-nothing-fails-to-innovate.md (100%) rename docs/discussions/docker-multi-stage-builds.md => writeups/implementing-docker-multi-stage-builds.md (100%) rename {docs/discussions => writeups}/microservice-reuse-and-authorization.md (100%) rename {docs/discussions => writeups}/reactive-programming.md (100%) rename {docs/discussions => writeups}/supporting-mac-os.md (100%) diff --git a/docs/discussions/masterclass.md b/docs/discussions/design-philosophy.md similarity index 69% rename from docs/discussions/masterclass.md rename to docs/discussions/design-philosophy.md index bc2ece7..59e70d9 100644 --- a/docs/discussions/masterclass.md +++ b/docs/discussions/design-philosophy.md @@ -81,59 +81,7 @@ To ensure quick onboarding, a lot of our docker-compose files following a naming
### Categories -These categories structure our re-usable code. - -#### Applications (`app-*`) -A full project, combining microservices into a full application. The structure is as seen above in [#Simple Mental Model](#simple-mental-model) - -#### Templates -These are a base for a custom built microservice with minimal overhead. From these you should be able to have a microservice going in your preferred language in minutes. - -Templates in the semantic.works stack allow... -- Minimal setup time -- Nudging (yet not enforcing) conventions -- Flexpoints: e.g. allowing template updates where new features & conventions get added with minimal effort - -There are also some guidelines to using a template most effectively: -- Ground truth in database - *(Caching is allowed, but keep in sync with the db)* -- Only talks to the database, not other microservices - *(If this happens, it's for a well thought out reason: it is the exception, not the rule)* -- Limit abstractions - *Keep the service easy to understand from a first read* -- Think about reuse - *(Split where others might reuse the service)* - - -To allow for easier development, the templates... -- Often support live reload, so you do not have to manually restart during development -- Don't require (nor recommend) cloning the template repository. Simply using the Dockerfile is enough, and allows for a cleaner codebase -- Some templates have a debugger on one of the ports - - -#### Services (`*-service` / `mu-*`) -##### Naming conventions: -- Core service: `mu-*` (e.g. *mu-dispatcher*) -- Regular service (repo): `*-service` (e.g. *acmidm-login-service*) -- Regular service (image): `org-name/*-service` (e.g. *lblod/acmidm-login-service*) -- Docker Compose name: `*` (e.g. *login*) - -(Configurable) services built to not require (a lot) more coding to implement a (generic) feature. For example, `mu-cl-resources` is an easily configurable solution in case you need to "list" resources (e.g. a shopping cart, people in a database...). Frontends are hard to re-use as your customer will probably want a specific feel or implementation, but universal features can be re-used to fit the needs of multiple projects. - -Services in the semantic.works stack should... -- Contain hareable/re-usable/runnable functionality (see the paragraph above) -- Require minimal configuration (sensible defaults are very okay) -- Persist its state in the database -- Be user facing (usually) - -Services can be found through... -- Searching for the `mu-service` topic on GitHub -- Finding projects extending the templates (e.g. `mu-javascript-template`) -- Asking around - - -#### Ember add-ons -Like the configurable services, but for Ember.js frontend components and functionalities. +We structure our projects into different categories for easier re-use and discoverability. Please check out [project-categories.md](project-categories.md) *This document has been adapted by Denperidge from Aad Versteden's masterclass 01 - How and why pt1, pt & pt3. You can view the original video [here]()* diff --git a/docs/discussions/project-categories.md b/docs/discussions/project-categories.md new file mode 100644 index 0000000..d90f7d8 --- /dev/null +++ b/docs/discussions/project-categories.md @@ -0,0 +1,57 @@ +# What are the project categories? +These categories structure our re-usable code. + + +## Applications (`app-*`) +A full project, combining microservices into a full application. An example structure can be seen below: +![A flow chart. Single Page App points to Identifier, Identifier to Dispatcher. Dispatcher points to 4 differently coloured blocks: they say Registration, Login, Resources, and Files. These all point towards a regular coloured block in between them. This block says Store & Sync, and it contains a Triplestore header, under which Ontology, Delta and Security are noted](../../assets/simple-mental-model-advanced.excalidraw.svg) + +## Templates +These are a base for a custom built microservice with minimal overhead. From these you should be able to have a microservice going in your preferred language in minutes. + +Templates in the semantic.works stack allow... +- Minimal setup time +- Nudging (yet not enforcing) conventions +- Flexpoints: e.g. allowing template updates where new features & conventions get added with minimal effort + +There are also some guidelines to using a template most effectively: +- Ground truth in database + *(Caching is allowed, but keep in sync with the db)* +- Only talks to the database, not other microservices + *(If this happens, it's for a well thought out reason: it is the exception, not the rule)* +- Limit abstractions + *Keep the service easy to understand from a first read* +- Think about reuse + *(Split where others might reuse the service)* + + +To allow for easier development, the templates... +- Often support live reload, so you do not have to manually restart during development +- Don't require (nor recommend) cloning the template repository. Simply using the Dockerfile is enough, and allows for a cleaner codebase +- Some templates have a debugger on one of the ports + + +## Services (`*-service` / `mu-*`) +### Naming conventions: +- Core service: `mu-*` (e.g. *mu-dispatcher*) +- Regular service (repo): `*-service` (e.g. *acmidm-login-service*) +- Regular service (image): `org-name/*-service` (e.g. *lblod/acmidm-login-service*) +- Docker Compose name: `*` (e.g. *login*) + +(Configurable) services built to not require (a lot) more coding to implement a (generic) feature. For example, `mu-cl-resources` is an easily configurable solution in case you need to "list" resources (e.g. a shopping cart, people in a database...). Frontends are hard to re-use as your customer will probably want a specific feel or implementation, but universal features can be re-used to fit the needs of multiple projects. + +Services in the semantic.works stack should... +- Contain hareable/re-usable/runnable functionality (see the paragraph above) +- Require minimal configuration (sensible defaults are very okay) +- Persist its state in the database +- Be user facing (usually) + +Services can be found through... +- Searching for the `mu-service` topic on GitHub +- Finding projects extending the templates (e.g. `mu-javascript-template`) +- Asking around + + +## Ember add-ons +Like the configurable services, but for Ember.js frontend components and functionalities. + diff --git a/docs/how-tos/creating-templates.md b/docs/how-tos/creating-templates.md new file mode 100644 index 0000000..6036492 --- /dev/null +++ b/docs/how-tos/creating-templates.md @@ -0,0 +1,3 @@ +# Creating templates + +Due to the complexity of creating a template (at least relatively to microservices & applications), a more in-depth tutorial is available at [../tutorials/creating-templates.md](../tutorials/creating-templates.md). diff --git a/docs/how-tos/documentation-quickstart.md b/docs/how-tos/quickstart-writing-documentation.md similarity index 100% rename from docs/how-tos/documentation-quickstart.md rename to docs/how-tos/quickstart-writing-documentation.md diff --git a/docs/references/template-requirements.md b/docs/tutorials/creating-templates.md similarity index 100% rename from docs/references/template-requirements.md rename to docs/tutorials/creating-templates.md diff --git a/docs/how-tos/developing-inside-containers.md b/docs/tutorials/developing-inside-containers.md similarity index 100% rename from docs/how-tos/developing-inside-containers.md rename to docs/tutorials/developing-inside-containers.md diff --git a/docs/discussions/experimentation.md b/writeups/all-or-nothing-fails-to-innovate.md similarity index 100% rename from docs/discussions/experimentation.md rename to writeups/all-or-nothing-fails-to-innovate.md diff --git a/docs/discussions/docker-multi-stage-builds.md b/writeups/implementing-docker-multi-stage-builds.md similarity index 100% rename from docs/discussions/docker-multi-stage-builds.md rename to writeups/implementing-docker-multi-stage-builds.md diff --git a/docs/discussions/microservice-reuse-and-authorization.md b/writeups/microservice-reuse-and-authorization.md similarity index 100% rename from docs/discussions/microservice-reuse-and-authorization.md rename to writeups/microservice-reuse-and-authorization.md diff --git a/docs/discussions/reactive-programming.md b/writeups/reactive-programming.md similarity index 100% rename from docs/discussions/reactive-programming.md rename to writeups/reactive-programming.md diff --git a/docs/discussions/supporting-mac-os.md b/writeups/supporting-mac-os.md similarity index 100% rename from docs/discussions/supporting-mac-os.md rename to writeups/supporting-mac-os.md From 797e070be20a9ea9b255bc2d8ce172f629bc484f Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 16 Oct 2023 09:51:07 +0200 Subject: [PATCH 42/52] Writeups structure refactor --- README.md | 31 ++++++++++++++----- .../all-or-nothing-fails-to-innovate.md | 0 .../reactive-programming.md | 0 publications.md => writeups/publications.md | 0 .../developerweek-2018.md | 0 .../{ => retrospectives}/dockercon-eu-2017.md | 0 writeups/{ => retrospectives}/oslo2.md | 0 ...implementing-docker-multi-stage-builds.md} | 0 ...w-microservice-reuse-and-authorization.md} | 0 .../sw-supporting-mac-os.md} | 0 10 files changed, 23 insertions(+), 8 deletions(-) rename writeups/{ => perspectives}/all-or-nothing-fails-to-innovate.md (100%) rename writeups/{ => perspectives}/reactive-programming.md (100%) rename publications.md => writeups/publications.md (100%) rename writeups/{ => retrospectives}/developerweek-2018.md (100%) rename writeups/{ => retrospectives}/dockercon-eu-2017.md (100%) rename writeups/{ => retrospectives}/oslo2.md (100%) rename writeups/{implementing-docker-multi-stage-builds.md => retrospectives/sw-implementing-docker-multi-stage-builds.md} (100%) rename writeups/{microservice-reuse-and-authorization.md => retrospectives/sw-microservice-reuse-and-authorization.md} (100%) rename writeups/{supporting-mac-os.md => retrospectives/sw-supporting-mac-os.md} (100%) diff --git a/README.md b/README.md index 677202b..bdc4765 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ This repository is used to store information and track issues that apply cross-services in the full semantic.works stack. If you do not know where to get started, check out any/all of the following -- +1. [discussions - design philosophy](docs/discussions/design-philosophy.md) +2. [discussions - project categories](docs/discussions/project-categories.md) +3. [how-tos - creating applications](docs/how-tos/creating-applications.md) or [how-tos - creating microservices](docs/how-tos/creating-microservices.md) ## Reference @@ -21,18 +23,31 @@ If you want more information behind the design of semantic.works, you can read t - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) - **The benefits of microservices through...** - - [Reactive programming](docs/discussions/reactive-programming.md) - [Smaller & readable code](docs/discussions/smaller-readable-code.md) - [Sharing authorization](docs/discussions/sharing-authorization.md) -- [Docker multi-stage builds benefits](docs/discussions/docker-multi-stage-builds.md) -- [Supporting MacOS](docs/discussions/supporting-mac-os.md) ## How-to - [Documentation quickstart](docs/how-tos/documentation-quickstart.md) -- [Develop with your local IDE and tools inside a Docker container](docs/how-tos/developing-inside-containers.md) - [Troubleshooting - Slow starting containers using 100% CPU](docs/how-tos/troubleshooting---slow-starting-containers.md) +## Tutorials +- [Develop with your local IDE and tools inside a Docker container](docs/tutorials/developing-inside-containers.md) + + ## Writeups -Our retrospectives on... -- [OSLO²](writeups/oslo2.md) -- [Dockercon EU 2017](writeups/dockercon-eu-2017.md) +Perspectives on... +- [Experimentation / all or nothing fails to innovate](writeups/perspectives/all-or-nothing-fails-to-innovate.md) +- [Reactive programming](docs/discussions/reactive-programming.md) + +Retrospectives on... +- [Dockercon EU 2017](writeups/retrospectives/dockercon-eu-2017.md) +- [Developerweek 2018](writeups/retrospectives/developerweek-2018.md) +- [OSLO²](writeups/retrospectives/oslo2.md) + +- [Semantic.works - Implementing Docker multi-stage builds benefits](writeups/retrospectives/sw-implementing-docker-multi-stage-builds.md) +- [Semantic.works - Microservice reuse and authorization](writeups/retrospectives/sw-microservice-reuse-and-authorization.md) +- [Semantic.works - Supporting MacOS](writeups/retrospectives/sw-supporting-mac-os.md) + +Or.. +- Discover who governs semantic.works in [who - mu-semtech](writeups/who---mu-semtech.md) +- Find our external publications in [publictations](writeups/publications.md) \ No newline at end of file diff --git a/writeups/all-or-nothing-fails-to-innovate.md b/writeups/perspectives/all-or-nothing-fails-to-innovate.md similarity index 100% rename from writeups/all-or-nothing-fails-to-innovate.md rename to writeups/perspectives/all-or-nothing-fails-to-innovate.md diff --git a/writeups/reactive-programming.md b/writeups/perspectives/reactive-programming.md similarity index 100% rename from writeups/reactive-programming.md rename to writeups/perspectives/reactive-programming.md diff --git a/publications.md b/writeups/publications.md similarity index 100% rename from publications.md rename to writeups/publications.md diff --git a/writeups/developerweek-2018.md b/writeups/retrospectives/developerweek-2018.md similarity index 100% rename from writeups/developerweek-2018.md rename to writeups/retrospectives/developerweek-2018.md diff --git a/writeups/dockercon-eu-2017.md b/writeups/retrospectives/dockercon-eu-2017.md similarity index 100% rename from writeups/dockercon-eu-2017.md rename to writeups/retrospectives/dockercon-eu-2017.md diff --git a/writeups/oslo2.md b/writeups/retrospectives/oslo2.md similarity index 100% rename from writeups/oslo2.md rename to writeups/retrospectives/oslo2.md diff --git a/writeups/implementing-docker-multi-stage-builds.md b/writeups/retrospectives/sw-implementing-docker-multi-stage-builds.md similarity index 100% rename from writeups/implementing-docker-multi-stage-builds.md rename to writeups/retrospectives/sw-implementing-docker-multi-stage-builds.md diff --git a/writeups/microservice-reuse-and-authorization.md b/writeups/retrospectives/sw-microservice-reuse-and-authorization.md similarity index 100% rename from writeups/microservice-reuse-and-authorization.md rename to writeups/retrospectives/sw-microservice-reuse-and-authorization.md diff --git a/writeups/supporting-mac-os.md b/writeups/retrospectives/sw-supporting-mac-os.md similarity index 100% rename from writeups/supporting-mac-os.md rename to writeups/retrospectives/sw-supporting-mac-os.md From b607190eda9ae92c6b1ee3e80af3d0d21985a3da Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 16 Oct 2023 10:10:35 +0200 Subject: [PATCH 43/52] Merged building-a-template into creating-templates --- docs/tutorials/building-a-template.md | 181 -------------------------- docs/tutorials/creating-templates.md | 159 +++++++++++++++++++++- 2 files changed, 152 insertions(+), 188 deletions(-) delete mode 100644 docs/tutorials/building-a-template.md diff --git a/docs/tutorials/building-a-template.md b/docs/tutorials/building-a-template.md deleted file mode 100644 index c640267..0000000 --- a/docs/tutorials/building-a-template.md +++ /dev/null @@ -1,181 +0,0 @@ -# So you want to build a template? -An explanation and motivation for templates, from a producer's point of view - -This specification describes the mindset we have for each mu.semte.ch -template. It describes the goals of a template, the soft constraints, -hints at method naming, and lists key features which a template should -have. - -## Why templates - -Templates make it easier to implement new microservices. They help you -get started with the technology you know and love, or with a new -technology. - -Templates should be predictable. The naming and structure of templates -should therefore match certain standards. Templates make it easy to get -started with the technology you know and love, or help you understand -the structure of a service written in a language you barely know. - -Templates make it easy to follow the constraints mu.semte.ch places on -services. They should therefore contain a basic set of features which -ensures users can easily develop applications in them. - -Templates are our way to familiarize users with mu.semte.ch and to -ensure users follow the best practices where possible. - -## Template features - -A template should make sure that the mu.semte.ch ecosystem keeps -running. The construction of a template happens in a set of stages. -First one should implement a basic service in the base technology, to -get a feeling of which technologies are suited for the template. Next up -is the construction of a PoC (Proof of Concept). Then comes the -standardisation, which requires some extra features to ensure the -template can be used in practice. - -- **You can check the requirements for the PoC [here](../references/template-requirements.md#poc-proof-of-concept-requirements)** -- **And the requirements for the full template [here](../references/template-requirements.md#full-template-requirements)** - - -## Levels of abstraction - -Most developers like to abstract things. Make sure that nothing is -repeated. Many of us like to get started quickly. These two ideas often -don't go hand-in-hand. We dismiss non-generic abstractions and require -consistent naming. - -Getting started should be trivial and has the highest priority. It -should be intuitive to start using a new template. It should be easy to -read code that was written in a template, even if it isn't yours. - -The following is a description of various topics on which consistency -should be held. The description should be adapted to the use of your -language. Ie: if you'd call sparql_query in Ruby, sparqlQuery in Java -and sparql-query in Common Lisp. - -### Defining HTTP endpoints - -Users will define HTTP endpoints to which their service will respond. -Separate methods should be defined for separate types of call. The -method should simply contain the name of HTTP verb, and the URL to which -we respond. The system should allow the user to destructure the input -and access the destructured contents. Calls may be defined in a block, -but if that is the case, then the block should be repeatable. - -**Essentials:** -- Use simple name of HTTP verb -- Allow straight-forward destructuring of URL -- Support finding query parameters - -Example in Common Lisp: - -```lisp -(defcall :get (“say” “hello” “to” name) - ;; responds to GET /say/hello/to/Aad?title=Sir - ;; name is bound to Aad - ;; (get-parameter “title”) yields “Sir” -) - -``` - -Example in Ruby: -```ruby -get “say/hello/to/:name” do |name| - # responds to GET /say/hello/to/Erika?title=Madam - # name is bound to erika - # params[:title] yields “Madam” -end - -``` - -Another example in Ruby: -```ruby -define_calls do |server| - server.get “say/hello/to/:name” do |name| - # responds to GET /say/hello/to/Erika?title=Madam - # name is bound to erika - # params[:title] yields “Madam” - end -end -``` - -### Executing SPARQL queries - -We should mitigate SPARQL-injection whilst still ensuring the SPARQL -queries are easy to recognise. Allow for injecting variables in a -SPARQL-query string. Support escaping as separate methods. Query results -should be parsed automatically so the user can easily process them. - -A SPARQL endpoint often differs in query and update endpoints. Hence we -advise to use those two names for executing the queries. - -For languages which don't allow injecting variables into strings, magic -is allowed. Keep it to an absolute minimum and try to mimic what is -commonly found in other languages. It should be something which you can -explain in a single sentence. - -Common Lisp example: -```lisp -(sparql-query “SELECT ?s ?p ?o WHERE - GRAPH { - ?s a foaf:Agent; - foaf:name ~A; - ?p ?o. - }”, (sparql-escape-string “Felix”)) - -(sparql-update “INSERT DATA - GRAPH { - ext:Aad a foaf:Agent. - }”) -``` -J - -Ruby example: -```ruby -query “SELECT ?s ?p ?o WHERE - GRAPH { - ?s a foaf:Agent; - foaf:name #{username.sparql_escape}; - ?p ?o. - }” -``` - -## Getting started - -Building a template without knowing the domain is near impossible. The -following steps guide you into building a usable template. - -First select the technology you would like to support. Look around for -web frameworks which match the ideology of microservices. Search for the -'Ruby Sinatra' competitor for your language. - -Your next challenge is to implement a simple microservice for this -purpose. As an example, you can write a service that returns the amount -of Tasks in the store, and which allows you to set a Task as done. The -service itself is not of importance, you'll learn lessons about the -framework when you implement the microservice. - -Don't forget to run your microservice in a toy mu.semte.ch project. -That way you'll know that it actually works. -[Mu-project](https://github.com/mu-semtech/mu-project) -offers a good starting point. - -Once your service works, you can start abstracting it. Much of the code -you've built would be used again when you implement another service in -the same language. Go over the list of the PoC, ensure you've -implemented each feature, and put this in a separate project. This -project will become the template to use for the Tasks service. If all -goes well, you'll end with a PoC template service, and a much shorter -Tasks service. - -Next up is going over the list of features needed for the full template. -Go over the list of features and try to implement them in a clean -fashion. More complex, and realistic microservices, like those -automatically creating Tasks for all starred books you haven't read yet -now become possible. This is the space where your microservice should be -in. - -Document the use of the microservice, and get us to publish it on our -GitHub space. - diff --git a/docs/tutorials/creating-templates.md b/docs/tutorials/creating-templates.md index 9e6bf80..1466f1f 100644 --- a/docs/tutorials/creating-templates.md +++ b/docs/tutorials/creating-templates.md @@ -1,6 +1,41 @@ -# Template requirements +# Creating templates -## PoC (proof of concept) requirements +## The importance of templates +Templates... +- ... make it easier to implement new microservices +- ... help you get started with the technology you know and love, or help you understand the structure of a service written in a technology/language you barely know +- ... should be predictable. The naming and structure of templates +should therefore match certain standards +- ... make it easy to follow the constraints semantic.works places on services, and should therefore contain a basic set of features which ensures users can easily develop applications in them + + +Templates are our way to familiarize users with semantic.works and to ensure users follow the best practices where possibl + +## The steps of developing a template +A template should make sure that the semantic.works ecosystem keeps +running. The construction of a template happens in a set of stages. + +Building a template without knowing the domain is near impossible. + +### Step 1: Basic service +First one should implement a basic service in the base technology, to get a feeling of which technologies are suited for the template + +To begin, select the technology you would like to support. Look around for web frameworks which match the ideology of microservices. Search for the 'Ruby Sinatra' competitor for your language. + +Your next challenge is to implement a simple microservice for this +purpose. As an example, you can write a service that returns the amount of Tasks in the store, and which allows you to set a Task as done. The service itself is not of importance, you'll learn lessons about the framework when you implement the microservice. + +Don't forget to run your microservice in a toy semantic.works project. +That way you'll know that it actually works. +[mu-project](https://github.com/mu-semtech/mu-project) +offers a good starting point. + +### Step 2: PoC (Proof of Concept) +Next up is the construction of a PoC (Proof of Concept). + +Once your service works, you can start abstracting it. Much of the code you've built would be used again when you implement another service in the same language. Go over the list of the PoC, ensure you've implemented each feature, and put this in a separate project. This project will become the template to use for the Tasks service. If all goes well, you'll end with a PoC template service, and a much shorter Tasks service. + +#### PoC requirements - Respond to HTTP requests - Parse JSON & JSONAPI payloads - Support implementation of GET PUT PATCH POST DELETE @@ -19,9 +54,20 @@ - Load user-code from `/app` - Download & install dependencies on startup -## Full template requirements -- [**All requirements for the PoC**](#poc-proof-of-concept-requirements) +### Step 3: Full template +Then comes the standardisation, which requires some extra features to ensure the template can be used in practice. + +This includes going over the [list of features needed for the full template](#full-requirements). +Go over the list of features and try to implement them in a clean +fashion. More complex, and realistic microservices, like those +automatically creating Tasks for all starred books you haven't read yet now become possible. This is the space where your microservice should be in. + +Document the use of the microservice, and get us to publish it on our GitHub space. + +#### Full requirements + +- [**All requirements for the PoC**](#poc-requirements) - Configuration - Access environment variables [[2]](#notes) @@ -51,7 +97,103 @@ - Enable debugging symbols (if possible) - Enable error output (if configurable) -## Helper functions + +## Extra considerations +### Levels of abstraction + +Most developers like to abstract things. Make sure that nothing is +repeated. Many of us like to get started quickly. These two ideas often don't go hand-in-hand. We dismiss non-generic abstractions and require consistent naming. + +Getting started should be trivial and has the highest priority. It +should be intuitive to start using a new template. It should be easy to read code that was written in a template, even if it isn't yours. + +The following is a description of various topics on which consistency should be held. The description should be adapted to the use of your language. Ie: if you'd call sparql_query in Ruby, sparqlQuery in Java and sparql-query in Common Lisp. + +#### Defining HTTP endpoints + +Users will define HTTP endpoints to which their service will respond. +Separate methods should be defined for separate types of call. The +method should simply contain the name of HTTP verb, and the URL to which we respond. The system should allow the user to destructure the input and access the destructured contents. Calls may be defined in a block, but if that is the case, then the block should be repeatable. + +**Essentials:** +- Use simple name of HTTP verb +- Allow straight-forward destructuring of URL +- Support finding query parameters + +Example in Common Lisp: + +```lisp +(defcall :get (“say” “hello” “to” name) + ;; responds to GET /say/hello/to/Aad?title=Sir + ;; name is bound to Aad + ;; (get-parameter “title”) yields “Sir” +) + +``` + +Example in Ruby: +```ruby +get “say/hello/to/:name” do |name| + # responds to GET /say/hello/to/Erika?title=Madam + # name is bound to erika + # params[:title] yields “Madam” +end + +``` + +Another example in Ruby: +```ruby +define_calls do |server| + server.get “say/hello/to/:name” do |name| + # responds to GET /say/hello/to/Erika?title=Madam + # name is bound to erika + # params[:title] yields “Madam” + end +end +``` + +#### Executing SPARQL queries + +We should mitigate SPARQL-injection whilst still ensuring the SPARQL +queries are easy to recognise. Allow for injecting variables in a +SPARQL-query string. Support escaping as separate methods. Query results +should be parsed automatically so the user can easily process them. + +A SPARQL endpoint often differs in query and update endpoints. Hence we +advise to use those two names for executing the queries. + +For languages which don't allow injecting variables into strings, magic +is allowed. Keep it to an absolute minimum and try to mimic what is +commonly found in other languages. It should be something which you can +explain in a single sentence. + +Common Lisp example: +```lisp +(sparql-query “SELECT ?s ?p ?o WHERE + GRAPH { + ?s a foaf:Agent; + foaf:name ~A; + ?p ?o. + }”, (sparql-escape-string “Felix”)) + +(sparql-update “INSERT DATA + GRAPH { + ext:Aad a foaf:Agent. + }”) +``` +J + +Ruby example: +```ruby +query “SELECT ?s ?p ?o WHERE + GRAPH { + ?s a foaf:Agent; + foaf:name #{username.sparql_escape}; + ?p ?o. + }” +``` + +### Helper functions Ideally, your template should expose some helper functions, including but not limited to the following. Extra parameters can be added as needed or wanted. @@ -68,7 +210,7 @@ needed or wanted. *Note: camelcase is used in this documentation. However, when writing the helpers in the language of your choice, use the notation that is in line with the language in question.* -### sparql_escape +#### sparql_escape The following helper functions all have the same description/funcionality, but for different types. As such, the description column is omitted. **Description:** Converts the given object to a SPARQL-safe RDF object string with the right RDF-datatype. @@ -85,7 +227,7 @@ This functions should be used especially when inserting user-input to avoid SPAR | sparql_escape_datetime | value: `datetime` | | sparql_escape_bool | value: `bool` | -### Passing headers +#### Passing headers The following headers should be passed when making queries: | Header name | Type | Description | @@ -96,6 +238,9 @@ The following headers should be passed when making queries: | MU-AUTH-USED-GROUPS | | | + + + ## Notes 1. This constraint should be dropped in the distant future, but it is From 6b9c9cb8a0c14fd045787b26a64f67cd48e1a764 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 16 Oct 2023 10:33:38 +0200 Subject: [PATCH 44/52] Merged naming-conventions into project categories --- docs/discussions/project-categories.md | 67 ++++++++++++++++++-------- docs/references/naming-conventions.md | 26 ---------- 2 files changed, 47 insertions(+), 46 deletions(-) delete mode 100644 docs/references/naming-conventions.md diff --git a/docs/discussions/project-categories.md b/docs/discussions/project-categories.md index d90f7d8..9e8e24b 100644 --- a/docs/discussions/project-categories.md +++ b/docs/discussions/project-categories.md @@ -1,14 +1,52 @@ # What are the project categories? -These categories structure our re-usable code. +These categories structure our re-usable code. The semantic.works framework consists of a number of components. A number that is growing steadily and will keep growing in the future. If you’re new to the stack, it’s not always easy to find your way through it. Therefore, we introduced different categories, findable through naming conventions. By adhering to these naming conventions we try to facilitate the hunt for the component you’re looking for. + +All the rules described below are just some simple and straightforward naming conventions, but adhering to them may drastically speed up the search process in the future. + ## Applications (`app-*`) A full project, combining microservices into a full application. An example structure can be seen below: ![A flow chart. Single Page App points to Identifier, Identifier to Dispatcher. Dispatcher points to 4 differently coloured blocks: they say Registration, Login, Resources, and Files. These all point towards a regular coloured block in between them. This block says Store & Sync, and it contains a Triplestore header, under which Ontology, Delta and Security are noted](../../assets/simple-mental-model-advanced.excalidraw.svg) -## Templates +## Core components (`mu-*`) +As the name reveals – core components are the core of the semantic.works framework. Without these components, the framework cannot function correctly. + +Core examples include [mu-identifier](https://github.com/mu-semtech/mu-identifier) and [mu-dispatcher](https://github.com/mu-semtech/mu-dispatcher). + + +## Services (`*-service`) +### Naming conventions: +- Repo: `*-service` (e.g. *acmidm-login-service*) +- Image: `org-name/*-service` (e.g. *lblod/acmidm-login-service*) +- Docker Compose name: `*` (e.g. *login*) + +In case a microservice needs to be configured in a specific programming language the name may also include the name (or an abbreviation) of the programming language name. For example, a service named export-ruby-service requires an export configuration in Ruby while export-js-service requires a configuration in javascript + + +Services are the configurable semantic microservices that compose the backend of an application, which don't require (a lot) more coding to implement a feature, generic or more specific. + +For example, `mu-cl-resources` is an easily configurable solution in case you need to "list" resources (e.g. a shopping cart, people in a database...). Frontends are hard to re-use as your customer will probably want a specific feel or implementation, but universal features can be re-used to fit the needs of multiple projects. + +Services in the semantic.works stack should... +- Contain hareable/re-usable/runnable functionality (see the paragraph above) +- Require minimal configuration (sensible defaults are very okay) +- Persist its state in the database +- Be user facing (usually) + + +Services can be found through... +- Searching for the `mu-service` topic on GitHub +- Finding projects extending the templates (e.g. `mu-javascript-template`) +- Asking around + + + +## Templates (`mu-*-template`) These are a base for a custom built microservice with minimal overhead. From these you should be able to have a microservice going in your preferred language in minutes. +Since the programming language of a template is important – you will need to develop the microservice in this language – the name of the language is included in the template name. + Templates in the semantic.works stack allow... - Minimal setup time - Nudging (yet not enforcing) conventions @@ -31,27 +69,16 @@ To allow for easier development, the templates... - Some templates have a debugger on one of the ports -## Services (`*-service` / `mu-*`) +## Ember add-ons (`ember-mu-*`/`ember-*`) ### Naming conventions: -- Core service: `mu-*` (e.g. *mu-dispatcher*) -- Regular service (repo): `*-service` (e.g. *acmidm-login-service*) -- Regular service (image): `org-name/*-service` (e.g. *lblod/acmidm-login-service*) -- Docker Compose name: `*` (e.g. *login*) +- We follow the Ember community naming convention by prefixing the package names with `ember-` +- To indicate when they are part of the semantic.works stack, we instead use `ember-mu-` (e.g. *ember-mu-login*) -(Configurable) services built to not require (a lot) more coding to implement a (generic) feature. For example, `mu-cl-resources` is an easily configurable solution in case you need to "list" resources (e.g. a shopping cart, people in a database...). Frontends are hard to re-use as your customer will probably want a specific feel or implementation, but universal features can be re-used to fit the needs of multiple projects. +Like the configurable services, but for Ember.js frontend components and functionalities. These are the building blocks for the frontend application. Basically, it are just NPM packages you can install in your Ember app. -Services in the semantic.works stack should... -- Contain hareable/re-usable/runnable functionality (see the paragraph above) -- Require minimal configuration (sensible defaults are very okay) -- Persist its state in the database -- Be user facing (usually) - -Services can be found through... -- Searching for the `mu-service` topic on GitHub -- Finding projects extending the templates (e.g. `mu-javascript-template`) -- Asking around +## Utilities +Utilities is the collective term of all tools that facilitate the initiation and development of a mu.semte.ch application. Since they are so diverse, they don’t follow a strict naming convention. The name just tries to cover the functionality. Nonetheless, similar utilities have a similar name. For example, all generators are postfixed with `-generator``. -## Ember add-ons -Like the configurable services, but for Ember.js frontend components and functionalities. +*This document includes content adapted from Erika Pauwels' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/06/22/find-your-way-through-the-stack/)* diff --git a/docs/references/naming-conventions.md b/docs/references/naming-conventions.md deleted file mode 100644 index d0026fb..0000000 --- a/docs/references/naming-conventions.md +++ /dev/null @@ -1,26 +0,0 @@ -# Naming conventions (Find your way through the stack) -![](http://mu.semte.ch/wp-content/uploads/2017/06/naming-1024x768.png) - -The mu.semte.ch framework consists of a number of components. A number that is growing steadily and will keep growing in the future. If you’re new to the stack, it’s not always easy to find your way through it. Therefore, we introduced some naming conventions. By adhering to these naming conventions we try to facilitate the hunt for the component you’re looking for. - -Roughly speaking, we distinguish between 5 types of components in the mu.semte.ch framework: - -* Core components -* Services -* Templates -* Ember addons -* Utilities - -**Core components** – as the name reveals – are the core of the mu.semte.ch framework. Without these components, the framework cannot function correctly. Each of the core components is prefixed with ‘mu-‘. Core examples include [mu-identifier](https://github.com/mu-semtech/mu-identifier) and [mu-dispatcher](https://github.com/mu-semtech/mu-dispatcher). - -**Services** are the semantic microservices that compose the backend of an application. Each microservice is postfixed with ‘-service’. A lot of microservices are [already available](https://mu.semte.ch/components/#services), like a login service, a registration service, an export service etc. In case a microservice needs to be configured in a specific programming language the name may also include the name (or an abbreviation) of the programming language name. For example, a service named export-ruby-service requires an export configuration in Ruby while export-js-service requires a configuration in javascript - -**Templates** are the starting points to build a microservice.  Since the programming language of a template is important – you will need to develop the microservice in this language – the name of the language is included in the template name. They are all postfixed with ‘-{language}-template’ like for example [mu-ruby-template](https://github.com/mu-semtech/mu-ruby-template) and [mu-javascript-template](https://github.com/mu-semtech/mu-javascript-template). - -**Ember addons** are the building blocks for the frontend application. Basically, it are just NPM packages you can install in your Ember app. We follow the Ember community naming convention by prefixing the package names with ’ember-‘.  To indicate they are part of the mu.semte.ch stack we also add ‘mu-‘ to the name. E.g. ember-mu-login. - -**Utilities** is the collective term of all tools that facilitate the initiation and development of a mu.semte.ch application. Since they are so diverse, they don’t follow a strict naming convention. The name just tries to cover the functionality. Nonetheless, similar utilities have a similar name. For example, all generators are postfixed with ‘-generator’. - -All the components are published on GitHub where they are also added tagged so you can easily find what you’re looking for. E.g. the core components are tagged with ‘mu-project’, while the services are tagged with ‘mu-service’ etc. All the rules described above are just some simple and straightforward naming conventions, but adhering to them may drastically speed up the search process in the future. - -*This document has been adapted from Erika Pauwels' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/06/22/find-your-way-through-the-stack/)* From f4324f54dc3f24e88c4d0ff83b25cb3856c77198 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 16 Oct 2023 10:35:20 +0200 Subject: [PATCH 45/52] Slight README changes, renamed references-reference --- README.md | 8 +++++--- docs/{references => reference}/commonly-used-headers.md | 0 .../representing-logged-in-users.md | 0 3 files changed, 5 insertions(+), 3 deletions(-) rename docs/{references => reference}/commonly-used-headers.md (100%) rename docs/{references => reference}/representing-logged-in-users.md (100%) diff --git a/README.md b/README.md index bdc4765..382448d 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,18 @@ # Semantic.works (mu-semtech) This repository is used to store information and track issues that apply cross-services in the full semantic.works stack. -If you do not know where to get started, check out any/all of the following +## Getting started +If you do not know where to begin, check out any/all of the following 1. [discussions - design philosophy](docs/discussions/design-philosophy.md) 2. [discussions - project categories](docs/discussions/project-categories.md) 3. [how-tos - creating applications](docs/how-tos/creating-applications.md) or [how-tos - creating microservices](docs/how-tos/creating-microservices.md) +4. [deploying applications](docs/how-tos/deploying-applications.md) ## Reference For technical information in semantic.works, you can see the following references: -- [Naming conventions](docs/references/naming-conventions.md) -- [Representing logged in users](docs/references/representing-logged-in-users.md) +- [Commonly used headers](docs/reference/commonly-used-headers.md) +- [Representing logged in users](docs/reference/representing-logged-in-users.md) ## Discussions If you want more information behind the design of semantic.works, you can read the following discussions: diff --git a/docs/references/commonly-used-headers.md b/docs/reference/commonly-used-headers.md similarity index 100% rename from docs/references/commonly-used-headers.md rename to docs/reference/commonly-used-headers.md diff --git a/docs/references/representing-logged-in-users.md b/docs/reference/representing-logged-in-users.md similarity index 100% rename from docs/references/representing-logged-in-users.md rename to docs/reference/representing-logged-in-users.md From f419e2a0e5f9abb2fd11f045d03af1a7e7843fdb Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 16 Oct 2023 11:07:59 +0200 Subject: [PATCH 46/52] Deprecations & refactors Deprecated: - mu-semtech-primer This document is functionally replaced by design-philosophy Refactors: - smaller-readable-code This document has gotten imported into design-philosophy - design-philosophy This document has gotten a few cleanups, changes & polishes --- docs/discussions/design-philosophy.md | 36 +++++++++---------- docs/discussions/smaller-readable-code.md | 28 --------------- .../perspectives}/mu-semtech-primer.md | 0 3 files changed, 16 insertions(+), 48 deletions(-) delete mode 100644 docs/discussions/smaller-readable-code.md rename {docs/discussions => writeups/perspectives}/mu-semtech-primer.md (100%) diff --git a/docs/discussions/design-philosophy.md b/docs/discussions/design-philosophy.md index 59e70d9..ffe5476 100644 --- a/docs/discussions/design-philosophy.md +++ b/docs/discussions/design-philosophy.md @@ -1,38 +1,32 @@ # Masterclass In this document, you will learn the following: -1. The core values of the semantic.works stack and their benefits -2. How our design decisions reflect those values -3. What this resulted in - +1. The [core values](#core-values) of the semantic.works stack and their benefits +2. How our [design decisions](#design-decisions) reflect those values +3. What [implementations](#implementations) this resulted in ## Core values -### Keep It Simple Stupid -#### 1. Productivity through simplicity +### 1. Productivity through simplicity - 1a) **Efficient development:** We wanted to get stuff done, without the requirement of being an expert on every topic, or creating a system that only the computer would understand -- 1b) **Clear overview:** Efficient development also comes in the form of overbloated applications, where you can easily lose track of what is handled where. We want to be able to look at a microservice and know immediately what it does. +- 1b) **Clear overview:** Efficient development also comes in the form of overbloated applications, where you can easily lose track of what is handled where. We want to be able to look at a service and know immediately what it does. +- 1c) **Less required reading**: There exists a saying in computer science that developers will end up with spending 90% of their time reading through code and 10% writing code. This reduces our effectiveness. For every line we write we will end up reading 10 lines first. +- 1d) **Allow reusing** -#### 2. Functionality through interoperability +### 2. Functionality through interoperability - 2a) **Maximize freedom:** There need to be rules: as few and liberating as possible. A lack of rules would cost interoperability, which would subsequently limit your freedom in the end. - 2b) **Expandability:** We also wanted orthogonal features: features that would extened eachother at no extra cost. - 2c) **Longevity:** Something built years ago should still work as expected. - -### Not getting stuck -#### 3. -
## Design decisions -### Reuse everything -- Small but focused services allow reusing everything ([1a](#1-productivity-through-simplicity)) - ### Micro -- Small but focused services allow reusing everything ([1a](#1-productivity-through-simplicity)) -- They are oftenly very easy to read, allowing a basic understanding even for those who do not know the language or framework in question -- Due to the small size of the services, debugging is easier: there are only a few hundred lines of code per service ([1a: efficient development & 1b: clear overview](#1-productivity-through-simplicity)). +- By isolating functionality by the boundaries of a microservice you get small but focused services: making them reusable easy to understand ([1a: efficient development](#1-productivity-through-simplicity)) +- They are oftenly very easy to read, allowing a basic understanding even for those who do not know the language or framework in question ([1c: less required reading](#1-productivity-through-simplicity)) +- Due to the small size of the services, debugging is easier: there are only a few hundred lines of code per service. Even if those 100 lines are important and take you longer to read, it reduces the area you need to look in ([1a: efficient development & 1b: clear overview](#1-productivity-through-simplicity)). ### Standard API's - Thanks to using technologies like JSON:API, services built years prior can still work as expected. ([2c: longevity](#2-functionality-through-interoperability)) +- Using these standards means more assumptions can be made and less code needs to be read ([1c: less required reading](#1-productivity-through-simplicity)) ### Centralised communication The microservices never talk to eachother directly. Instead they use a **shared linked-data** database. This is complemented by microservices using a semantic model. ([2a: maximize freedom & 2b: expandability](#2-functionality-through-interoperability)) @@ -41,7 +35,7 @@ The microservices never talk to eachother directly. Instead they use a **shared ![A venndiagram of two circles. The registration service circle encapsulates E-mail, Date of birth, Username and Password. The login service circle goes over Username and Password](../../assets/shared-data-models.excalidraw.svg) Semantic models have a bunch of advantages: - They can easily be appended upon ([2b: expandability](#2-functionality-through-interoperability)) -- They allow re-using the same models: the same model can be used for username/password, OAuth, or even mock logins ([2a: Maximize freedom, 2b: Expandability](#2-functionality-through-interoperability)) +- They allow re-using the same models: the same model can be used for username/password, OAuth, or even mock logins ([1a: efficient development, 1d: allow reusing](#1-productivity-through-simplicity), [2a: maximize freedom, 2b: expandability](#2-functionality-through-interoperability)) ## Implementations @@ -84,4 +78,6 @@ To ensure quick onboarding, a lot of our docker-compose files following a naming We structure our projects into different categories for easier re-use and discoverability. Please check out [project-categories.md](project-categories.md) -*This document has been adapted by Denperidge from Aad Versteden's masterclass 01 - How and why pt1, pt & pt3. You can view the original video [here]()* +*This document has been adapted by Denperidge from...* +- *Aad Versteden's masterclass video series. You can view the original video [here]()* +- *Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/08/31/how-mu-semte-ch-can-help-you-beat-the-10-odds/)* diff --git a/docs/discussions/smaller-readable-code.md b/docs/discussions/smaller-readable-code.md deleted file mode 100644 index e7340db..0000000 --- a/docs/discussions/smaller-readable-code.md +++ /dev/null @@ -1,28 +0,0 @@ -# Smaller & readable code (How mu.semte.ch can help you beat the 10% odds) -There exists a saying in computer science that developers will end up with spending 90% of their time reading through code and 10% writing code. This reduces our effectiveness. - -For every line we write we will end up reading 10 lines first. So a lot of languages like Clojure/Perl/Ruby use this as an argument to state that writing less code equals having less to read. Hence faster development. But are there more ways we can help? - -![](http://mu.semte.ch/wp-content/uploads/2017/08/emacs1percent.png) - -## Consistent abstractions -[mu.semte.ch](http://mu.semte.ch/) helps you build consistent abstractions. We base ourselves on standards like JSONAPI, SPARQL and HTTP. This means more assumptions can be made and less code needs to be read and (because of standards) the assumptions hold for a long time. - -Functionality should be reusable. Isolating functionality by the boundaries of a microservice makes it reusable and easy to understand. - -## Once upon a time -Think back at the time when you were a junior dev. You were set out to fix your first bug on a real project. You sit down, a hundred files staring at you. Good luck. -Which file to choose? With a good system you may be able to launch a debugger and find a starting point, if you received the time to set up development environment. In a perfect code base you go through a few level of abstractions learning each of them to find the issue at point. Reality often shows that even perfect code is hard to get. - -Can we make it slightly simpler? - -In [mu.semte.ch](http://mu.semte.ch/) there is simple code and well known standards. Spaghetti is not an option. A microservice is typically 100 lines of code. - -You are again a junior dev, set out to fix your fist bug. You open the dispatcher, see the relevant microservice, 100 lines of code, all included. - -## Conclusion -We are not saying that we don’t have bugs, we are not saying that nothing else can be wrong but just about all cases this is the way we work. And yes maybe these 100 lines are important and maybe they take you longer to read. However the chance of you quickly finding the bug greatly increases. - -Nice first day of work! - -*This document has been adapted from Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/08/31/how-mu-semte-ch-can-help-you-beat-the-10-odds/)* diff --git a/docs/discussions/mu-semtech-primer.md b/writeups/perspectives/mu-semtech-primer.md similarity index 100% rename from docs/discussions/mu-semtech-primer.md rename to writeups/perspectives/mu-semtech-primer.md From 8e913f44d85aff36b32db056f8d4da476952aa3c Mon Sep 17 00:00:00 2001 From: Denperidge Date: Mon, 16 Oct 2023 12:04:47 +0200 Subject: [PATCH 47/52] why-semantic-*: deprecate & import, README update why semantic-tech has been moved to writeups why-semantic-microservices has been moved to writeups, with the exception of one of the last paragraphs being added into the design-philosophy document --- README.md | 49 +++++++++++-------- docs/discussions/design-philosophy.md | 8 +-- .../why-semantic-microservices.md | 0 .../perspectives}/why-semantic-tech.md | 0 4 files changed, 34 insertions(+), 23 deletions(-) rename {docs/discussions => writeups/perspectives}/why-semantic-microservices.md (100%) rename {docs/discussions => writeups/perspectives}/why-semantic-tech.md (100%) diff --git a/README.md b/README.md index 382448d..92aa576 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,34 @@ This repository is used to store information and track issues that apply cross-services in the full semantic.works stack. ## Getting started -If you do not know where to begin, check out any/all of the following +If you do not know where to begin, check out any/all of the following documents: 1. [discussions - design philosophy](docs/discussions/design-philosophy.md) 2. [discussions - project categories](docs/discussions/project-categories.md) 3. [how-tos - creating applications](docs/how-tos/creating-applications.md) or [how-tos - creating microservices](docs/how-tos/creating-microservices.md) 4. [deploying applications](docs/how-tos/deploying-applications.md) +## How-to +### Create... +- [Applications](docs/how-tos/creating-applications.md) +- [Microservices](docs/how-tos/creating-microservices.md) +- [Templates](#create-1) + +### Development +- [Add services to your project](docs/how-tos/adding-services-to-your-project.md) +- [Deploy applications](docs/how-tos/deploying-applications.md) +- [Quickstart writing documentation](docs/how-tos/quickstart-writing-documentation.md) + +### Troubleshooting +- [Troubleshooting - Slow starting containers using 100% CPU](docs/how-tos/troubleshooting---slow-starting-containers.md) + +## Tutorials +### Create... +- [Templates](docs/tutorials/creating-templates.md) + +### Development +- [Develop with your local IDE and tools inside a Docker container](docs/tutorials/developing-inside-containers.md) + ## Reference For technical information in semantic.works, you can see the following references: - [Commonly used headers](docs/reference/commonly-used-headers.md) @@ -16,30 +37,18 @@ For technical information in semantic.works, you can see the following reference ## Discussions If you want more information behind the design of semantic.works, you can read the following discussions: -- **Why semantic...** - - [... technology?](docs/discussions/why-semantic-tech.md) - - [... microservices?](docs/discussions/why-semantic-microservices.md) -- [mu.semte.ch primer](docs/discussions/mu-semtech-primer.md) -- [semantic.works' documentation structure](docs/discussions/documentation-structure.md) -- **Experimentation** - - [All or nothing fails to innovate](docs/discussions/experimentation.md#all-or-nothing-fails-to-innovate) - - [mu.semte.ch as the ideal playground](docs/discussions/experimentation.md#musemtech-as-the-ideal-playground) -- **The benefits of microservices through...** - - [Smaller & readable code](docs/discussions/smaller-readable-code.md) +- [Design philosophy](docs/discussions/design-philosophy.md) +- [Documentation structure](docs/discussions/documentation-structure.md) +- [Project categories](docs/discussions/project-categories.md) - [Sharing authorization](docs/discussions/sharing-authorization.md) -## How-to -- [Documentation quickstart](docs/how-tos/documentation-quickstart.md) -- [Troubleshooting - Slow starting containers using 100% CPU](docs/how-tos/troubleshooting---slow-starting-containers.md) - -## Tutorials -- [Develop with your local IDE and tools inside a Docker container](docs/tutorials/developing-inside-containers.md) - - ## Writeups Perspectives on... - [Experimentation / all or nothing fails to innovate](writeups/perspectives/all-or-nothing-fails-to-innovate.md) -- [Reactive programming](docs/discussions/reactive-programming.md) +- [mu.semte.ch primer](writeups/perspectives/mu-semtech-primer.md) +- [Reactive programming](writeups/perspectives/) +- [Why semantic microservices](writeups/perspectives/why-semantic-microservices.md) +- [Why semantic tech](writeups/perspectives/why-semantic-tech.md) Retrospectives on... - [Dockercon EU 2017](writeups/retrospectives/dockercon-eu-2017.md) diff --git a/docs/discussions/design-philosophy.md b/docs/discussions/design-philosophy.md index ffe5476..ad56acf 100644 --- a/docs/discussions/design-philosophy.md +++ b/docs/discussions/design-philosophy.md @@ -28,15 +28,16 @@ In this document, you will learn the following: - Thanks to using technologies like JSON:API, services built years prior can still work as expected. ([2c: longevity](#2-functionality-through-interoperability)) - Using these standards means more assumptions can be made and less code needs to be read ([1c: less required reading](#1-productivity-through-simplicity)) -### Centralised communication -The microservices never talk to eachother directly. Instead they use a **shared linked-data** database. This is complemented by microservices using a semantic model. ([2a: maximize freedom & 2b: expandability](#2-functionality-through-interoperability)) - ### Semantic models ![A venndiagram of two circles. The registration service circle encapsulates E-mail, Date of birth, Username and Password. The login service circle goes over Username and Password](../../assets/shared-data-models.excalidraw.svg) Semantic models have a bunch of advantages: - They can easily be appended upon ([2b: expandability](#2-functionality-through-interoperability)) - They allow re-using the same models: the same model can be used for username/password, OAuth, or even mock logins ([1a: efficient development, 1d: allow reusing](#1-productivity-through-simplicity), [2a: maximize freedom, 2b: expandability](#2-functionality-through-interoperability)) +### Centralised communication +Microservices use a semantic model ([as seen above](#semantic-models)), but this is complemented by the fact that the microservices never talk to eachother directly. Instead they use a **shared linked-data** database. This replaces our need for a message queue. One can of course point out that such a message queue has several advantages, such as speed. But it also comes with a hidden disadvantage: namely every action taken or message sent has to be formatted in some way. This is yet again another dependency in your software system. Any microservice that you want include in your system and that needs to process the messages from certain other components will need to be developed with the knowledge of that message model in mind. ([2a: maximize freedom & 2b: expandability](#2-functionality-through-interoperability)) + + ## Implementations ### Simple Mental Model @@ -81,3 +82,4 @@ We structure our projects into different categories for easier re-use and discov *This document has been adapted by Denperidge from...* - *Aad Versteden's masterclass video series. You can view the original video [here]()* - *Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/08/31/how-mu-semte-ch-can-help-you-beat-the-10-odds/)* +*another Jonathan Langens' mu.semte.ch article. You can view it [here](https://mu.semte.ch/2017/06/15/semantic-micro-services-why-bother/)* diff --git a/docs/discussions/why-semantic-microservices.md b/writeups/perspectives/why-semantic-microservices.md similarity index 100% rename from docs/discussions/why-semantic-microservices.md rename to writeups/perspectives/why-semantic-microservices.md diff --git a/docs/discussions/why-semantic-tech.md b/writeups/perspectives/why-semantic-tech.md similarity index 100% rename from docs/discussions/why-semantic-tech.md rename to writeups/perspectives/why-semantic-tech.md From 91f4b8f08f476400eefea7626b3abe35d5869057 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Wed, 18 Oct 2023 12:03:22 +0200 Subject: [PATCH 48/52] Added explainers with getting started As suggested by @piemonkey --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 92aa576..3df3e39 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,10 @@ This repository is used to store information and track issues that apply cross-s ## Getting started If you do not know where to begin, check out any/all of the following documents: -1. [discussions - design philosophy](docs/discussions/design-philosophy.md) -2. [discussions - project categories](docs/discussions/project-categories.md) -3. [how-tos - creating applications](docs/how-tos/creating-applications.md) or [how-tos - creating microservices](docs/how-tos/creating-microservices.md) -4. [deploying applications](docs/how-tos/deploying-applications.md) +1. Read about why and how the semantic.works stack works: [discussions - design philosophy](docs/discussions/design-philosophy.md) +2. Recognising our different project types and how they help you: [discussions - project categories](docs/discussions/project-categories.md) +3. Learn how to [create a full-fledged application](docs/how-tos/creating-applications.md) or a [microservice to support it](docs/how-tos/creating-microservices.md) +4. Learn how to ideally [deploy your applications](docs/how-tos/deploying-applications.md) ## How-to From 584d5a2c67ada30fc434b79b2726f9e3ff08a4e9 Mon Sep 17 00:00:00 2001 From: Denperidge Date: Wed, 18 Oct 2023 12:46:38 +0200 Subject: [PATCH 49/52] Design philosophy polish --- docs/discussions/design-philosophy.md | 40 ++++++++++++++------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/docs/discussions/design-philosophy.md b/docs/discussions/design-philosophy.md index ad56acf..58b3e59 100644 --- a/docs/discussions/design-philosophy.md +++ b/docs/discussions/design-philosophy.md @@ -1,38 +1,40 @@ -# Masterclass +# Design philosophy In this document, you will learn the following: -1. The [core values](#core-values) of the semantic.works stack and their benefits -2. How our [design decisions](#design-decisions) reflect those values -3. What [implementations](#implementations) this resulted in +1. The [core values](#core-values) of the semantic.works stack and their benefits. +2. How our [design decisions](#design-decisions) reflect those values. +3. What [implementations](#implementations) this resulted in. ## Core values ### 1. Productivity through simplicity -- 1a) **Efficient development:** We wanted to get stuff done, without the requirement of being an expert on every topic, or creating a system that only the computer would understand -- 1b) **Clear overview:** Efficient development also comes in the form of overbloated applications, where you can easily lose track of what is handled where. We want to be able to look at a service and know immediately what it does. -- 1c) **Less required reading**: There exists a saying in computer science that developers will end up with spending 90% of their time reading through code and 10% writing code. This reduces our effectiveness. For every line we write we will end up reading 10 lines first. -- 1d) **Allow reusing** +- **1a) *Readability***: We wanted to get stuff done, without the requirement of being an expert on every topic, or creating a system that only the computer would understand. +- **1b) *Clear overview***: Efficient development also comes in the form of avoiding overbloated applications, where you can easily lose track of what is handled where. We want to be able to look at a project and know immediately what it does. +- **1c) *Less required reading***: There exists a saying in computer science that developers will end up with spending 90% of their time reading through code and 10% writing code. This reduces our effectiveness. For every line we write we will end up reading 10 lines first. So the question is: can we reduce the amount of lines that have to be written? ### 2. Functionality through interoperability -- 2a) **Maximize freedom:** There need to be rules: as few and liberating as possible. A lack of rules would cost interoperability, which would subsequently limit your freedom in the end. -- 2b) **Expandability:** We also wanted orthogonal features: features that would extened eachother at no extra cost. -- 2c) **Longevity:** Something built years ago should still work as expected. +- **2a) *Maximize freedom***: There need to be rules: as few and liberating as possible. A lack of rules would come at the cost of interoperability, which would subsequently limit your freedom in the end. +- **2b) *Expandability***: We also wanted orthogonal features: features that would extened eachother at no extra cost. +- **2c) *Longevity***: Something built years ago should still work as expected. +- **2d) *Allow reusing***: Re-writing the same boilerplate and/or copy-pasting functionality for every project is a waste of time.
## Design decisions ### Micro -- By isolating functionality by the boundaries of a microservice you get small but focused services: making them reusable easy to understand ([1a: efficient development](#1-productivity-through-simplicity)) +- By isolating functionality by the boundaries of a microservice you get small but focused services: making them reusable & easy to understand +([1a: readability, 1b: clear overview](#1-productivity-through-simplicity), +[2d: allow reusing](#2-functionality-through-interoperability)) - They are oftenly very easy to read, allowing a basic understanding even for those who do not know the language or framework in question ([1c: less required reading](#1-productivity-through-simplicity)) -- Due to the small size of the services, debugging is easier: there are only a few hundred lines of code per service. Even if those 100 lines are important and take you longer to read, it reduces the area you need to look in ([1a: efficient development & 1b: clear overview](#1-productivity-through-simplicity)). +- Due to the small size of the services, debugging is easier: there are only a few hundred lines of code per service. Even if those 100 lines are important and take you longer to read, it reduces the area you need to look in ([1a: readability & 1b: clear overview](#1-productivity-through-simplicity)). -### Standard API's -- Thanks to using technologies like JSON:API, services built years prior can still work as expected. ([2c: longevity](#2-functionality-through-interoperability)) +### Standard API's +- Thanks to using technologies like HTTP and JSON:API, different services built years prior can still work as expected, no matter how they're built internally. ([2a: maximize freedom, 2c: longevity](#2-functionality-through-interoperability)) - Using these standards means more assumptions can be made and less code needs to be read ([1c: less required reading](#1-productivity-through-simplicity)) ### Semantic models ![A venndiagram of two circles. The registration service circle encapsulates E-mail, Date of birth, Username and Password. The login service circle goes over Username and Password](../../assets/shared-data-models.excalidraw.svg) Semantic models have a bunch of advantages: -- They can easily be appended upon ([2b: expandability](#2-functionality-through-interoperability)) -- They allow re-using the same models: the same model can be used for username/password, OAuth, or even mock logins ([1a: efficient development, 1d: allow reusing](#1-productivity-through-simplicity), [2a: maximize freedom, 2b: expandability](#2-functionality-through-interoperability)) +- They can easily be appended upon, meaning there's no limitation to what data you can store ([2a: maximize freedom, 2b: expandability](#2-functionality-through-interoperability)) +- They allow re-using the same models: the same model can be used for username/password, OAuth, or even mock logins ([2a: maximize freedom, 2b: expandability, 2d: allow reusing](#2-functionality-through-interoperability)) ### Centralised communication Microservices use a semantic model ([as seen above](#semantic-models)), but this is complemented by the fact that the microservices never talk to eachother directly. Instead they use a **shared linked-data** database. This replaces our need for a message queue. One can of course point out that such a message queue has several advantages, such as speed. But it also comes with a hidden disadvantage: namely every action taken or message sent has to be formatted in some way. This is yet again another dependency in your software system. Any microservice that you want include in your system and that needs to process the messages from certain other components will need to be developed with the knowledge of that message model in mind. ([2a: maximize freedom & 2b: expandability](#2-functionality-through-interoperability)) @@ -46,7 +48,7 @@ Microservices use a semantic model ([as seen above](#semantic-models)), but this #### In-depth ![A flow chart. Single Page App points to Identifier, Identifier to Dispatcher. Dispatcher points to 4 differently coloured blocks: they say Registration, Login, Resources, and Files. These all point towards a regular coloured block in between them. This block says Store & Sync, and it contains a Triplestore header, under which Ontology, Delta and Security are noted](../../assets/simple-mental-model-advanced.excalidraw.svg) -([micro](#micro)) +([micro](#micro), [centralised communication](#centralised-communication)) 1. The **identifier** will create a session cookie. This doesn't identify you as a person (as this depends on what is in the database), but it gives a general hook ([centralised communication](#centralised-communication)) 2. The **dispatcher** will decide which **microservice** will be called for the made request @@ -57,7 +59,7 @@ For example: this setup also means that the other services don't care what regis ### Limited base technologies - **HTTP for communication:** If you build web application, you're probably already familiar with HTTP. ([Standard api's](#standard-apis)) - **JSON(:API) for sending data:** any programming language or framework that can read the widely known and used JSON will be able to be used. We then selected JSON:API to ensure consistency in our structure ([Standard api's](#standard-apis)) -- **SPARQL for storing data:** SPARQL queries are also just strings: any technology that can manage JSON will be able to use SPARQL in one way or another. This way we can store all our data as linked data, whilst enjoying the compatibility of JSON. +- **SPARQL for storing data:** SPARQL queries are also just strings: any technology that can manage JSON will be able to use SPARQL in one way or another. This way we can store all our data as linked data, whilst enjoying the compatibility of JSON. ([Semantic models](#semantic-models)) ### Docker & Docker-Compose From c19996e6ecde92c3b8d8e18f398d7ff89428e5fe Mon Sep 17 00:00:00 2001 From: Denperidge Date: Thu, 19 Oct 2023 16:34:46 +0200 Subject: [PATCH 50/52] Fixed outdated relative links --- docs/discussions/documentation-structure.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/discussions/documentation-structure.md b/docs/discussions/documentation-structure.md index d507b21..c4b0ecd 100644 --- a/docs/discussions/documentation-structure.md +++ b/docs/discussions/documentation-structure.md @@ -17,7 +17,7 @@ If we're copying documentation/text across places, we're doing something wrong: - This requires extra upkeep/work, breaching [(3) ease of use: writers](#3-ease-of-use-writers) ### 2. All-encompassing -Many documentations are incredibly lacking, as you've probably experienced over your time as a developer. The divio documentation standard, which you can view on [documentation.divio.com](https://documentation.divio.com/), attempts to clearly outline *what* should be documented, and *where* you can organise it. While I recommend viewing the talk and/or text on their website, a [quick start is included in this repository's how-tos](../how-tos/documentation-quickstart.md). +Many documentations are incredibly lacking, as you've probably experienced over your time as a developer. The divio documentation standard, which you can view on [documentation.divio.com](https://documentation.divio.com/), attempts to clearly outline *what* should be documented, and *where* you can organise it. While I recommend viewing the talk and/or text on their website, a [quick start is included in this repository's how-tos](../how-tos/quickstart-writing-documentation.md). ### 3. Ease of use: writing Minimising maintenance for the people who make the code is essential. Lots of people do not have the time, resources, training, or quite simply the desire to write down docs. This is one of the reasons we stay away from external documentation sites like gitbook: having them sign in to a whole different platform to write the documentation for their code would be way too much friction. This would also make us reliant on the external documentation provider, breaching [(6) Non-proprietary](#6-non-proprietary), and would require navigating to an external location for readers and writers alike, thus additionally breaching [(4) Ease of use: reading](#4-ease-of-use-reading). @@ -45,7 +45,7 @@ All documentation ends up in one of the following places - If it's something useful for people getting started with the semantic works stack as a whole, it should go in the `mu-project` repository. - If it's about a specific microservice or project, it should go inside of that projects repository -The documentation should ideally be written as presented in [project/how-tos/documentation-quickstart](../how-tos/documentation-quickstart.md). This also covers how to tag new revisions. +The documentation should ideally be written as presented in [project/how-tos/quickstart-writing-documentation](../how-tos/quickstart-writing-documentation.md). This also covers how to tag new revisions. For microservice developers and most people working on semantic.works, this is all you have to do! This will ensure that most design principles are met. However, to have a more readily available & traditionally structured documentation, the following steps get executed. From b82bc79fa68245dff35f0316694a00a9a1a483df Mon Sep 17 00:00:00 2001 From: Cat Date: Sat, 28 Oct 2023 15:01:26 +0200 Subject: [PATCH 51/52] Update quickstart-writing-documentation.md --- docs/how-tos/quickstart-writing-documentation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/how-tos/quickstart-writing-documentation.md b/docs/how-tos/quickstart-writing-documentation.md index 7e3426b..c425b20 100644 --- a/docs/how-tos/quickstart-writing-documentation.md +++ b/docs/how-tos/quickstart-writing-documentation.md @@ -2,6 +2,7 @@ - Our documentation follows the amazing documentation system used at Divio. [You can view the 30min talk and/or documentation on their website](https://documentation.divio.com/), and/or use the [checklist below](#checklist) to see if the documentation you've written fits. - The docs... - ... about semantic microservices/mu-semtech as a whole should be located in this repository ([mu-semtech/project](https://github.com/mu-semtech/project)). + - ... that would be good for a first-timer should be located in [the mu-semtech/mu-project repo](https://github.com/mu-semtech/mu-project)) - ... concerning specific microservices should be located inside their respective repositories. From fd84cd5dd4f3f977e07c61e9673fec0ce809fbb2 Mon Sep 17 00:00:00 2001 From: Cat Date: Sat, 28 Oct 2023 15:01:38 +0200 Subject: [PATCH 52/52] Fixed typo --- docs/how-tos/quickstart-writing-documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-tos/quickstart-writing-documentation.md b/docs/how-tos/quickstart-writing-documentation.md index c425b20..239a695 100644 --- a/docs/how-tos/quickstart-writing-documentation.md +++ b/docs/how-tos/quickstart-writing-documentation.md @@ -2,7 +2,7 @@ - Our documentation follows the amazing documentation system used at Divio. [You can view the 30min talk and/or documentation on their website](https://documentation.divio.com/), and/or use the [checklist below](#checklist) to see if the documentation you've written fits. - The docs... - ... about semantic microservices/mu-semtech as a whole should be located in this repository ([mu-semtech/project](https://github.com/mu-semtech/project)). - - ... that would be good for a first-timer should be located in [the mu-semtech/mu-project repo](https://github.com/mu-semtech/mu-project)) + - ... that would be good for a first-timer should be located in [the mu-semtech/mu-project repo](https://github.com/mu-semtech/mu-project) - ... concerning specific microservices should be located inside their respective repositories.