tag:blogger.com,1999:blog-56598667534868938482024-03-13T01:33:49.581-04:00scabl<b>Scalable Blog.</b>Unknownnoreply@blogger.comBlogger73125tag:blogger.com,1999:blog-5659866753486893848.post-12611220992657854262018-07-04T23:16:00.000-04:002018-07-04T23:16:31.364-04:00Two Major Design FlawsThroughout the time I spent developing <a href="http://longevityframework.org/">longevity</a>, I've made any number of major design flaws. The vision I started out with has changed drastically over time. It's evolved as I experimented with developing the longevity API. While my core guiding principles have stayed in place, the early versions of the library are nearly unrecognizable when compared to the latest version.<br />
<br />
Since the beginning, I've had a comprehensive test suite, that has allowed me to correct mistakes and simplify my API with ease and assurance of correctness. And working with the API as it has grown has allowed me to spot problems as I moved forward. But there were two major design flaws that remain in place. Both of them have been nagging me for many months now, until they have become clear in my head. Now I'm ready to admit and address these two errors: the failure to use <a href="https://github.com/milessabin/shapeless">shapeless</a>, and the lack of column-oriented storage of persistent entities with column-oriented back ends such as <a href="http://cassandra.apache.org/">Cassandra</a> and <a href="https://www.sqlite.org/index.html">SQLite</a>.<br />
<br />
<a name='more'></a><br />
Early on in development, I was considering whether or not to use shapeless. I decided against it, and I've been regretting this decision for a while now. But it would be easy to blame myself for it, in hindsight. At the time, I think it was not an unreasonable decision. This was back in the latter part of 2014. While I was an experienced Scala developer by this time, I was still inexperienced with shapeless, and other <a href="https://typelevel.org/">typelevel</a> libraries. I still relied heavily on my Java background, and I still thought of Scala as a sort of Java++. I just didn't get shapeless. I tried to understand it, but it was a bit beyond my abilities. And it was hard to find reading materials back then that would help me understand it. Since then, I've experimented with <a href="https://typelevel.org/cats/">cats</a>, shapeless, and other typelevel libraries, and I am more comfortable and accepting of them. <a href="https://github.com/underscoreio/shapeless-guide">The Type Astronaut's Guide to Shapeless</a> came out, which I read, and for the first time, I felt like a vaguely understood how shapeless actually worked. I've since put in a minor shapeless-related PR to <a href="https://github.com/typelevel/frameless">frameless</a>, and adopted shapeless in longevity for test data generation. All in all, I'm much more comfortable with shapeless and category theory now, even though I still consider myself a beginner in these areas.<br />
<br />
My own unfamiliarity and reluctance to jump up to the next level of functional programming wasn't the only thing that caused me to decide against using shapeless in longevity. I wanted to make a persistence library for Scala that was accessible to Java programmers, and at the time, I felt that libraries like <a href="https://github.com/scalaz/scalaz">Scalaz</a> and shapeless had the potential to alienate Java expats. The use of <a href="http://scalamacros.org/">Scala macros</a> would also complicate the builds of people using longevity, which I wanted to avoid at the time, even though I adopted macros later on myself.<br />
<br />
By this point, I recognize my avoidance of shapeless as a major architectural mistake. It would have improved my codebase by relying on a tried and true reflective library, rather than building my own home grown reflective tools. The benefits of pure compile-time reflection over mostly run-time reflection would have been significant. And it would probably have generated more interest among the potential user community. I'm now ready to migrate the entire library to shapeless, although of course, this will take some time.<br />
<br />
I want to talk a bit about the second major longevity design flaw that I have yet to correct. When I started writing longevity, the only back end I supported (aside from an in-memory back end that is intended for use in testing only) was <a href="https://www.mongodb.com/">MongoDB</a>. Naturally, I was storing the persistent entities in a JSON format. I found myself working on a contract where we were using Cassandra, and storing entities as JSON in a Cassandra column. I immediately saw how I could add a Cassandra back end to longevity that matched this style. The entities are stored as JSON in a column, and other columns are added for things like indexing and optimistic concurrency control. Later came the SQLite back end, and it followed the same approach.<br />
<br />
Over time, it became more and more clear that this was not the best solution. One important piece of feedback in this regard came whenever I was on a job that required the use of a Scala database integration layer. Every time this happened, I ruled out longevity as a possibility. Why? Because the database longevity was reading from and writing to, had to be used by other tools within the environment. And these other tools were expecting the data to be represented in a columnar format! Obviously, I had made a mistake here. It's certainly correctable, and I intend to correct it. But again, it will take some time.<br />
<br />
The first three years I was working on longevity, I was largely working on it full time. I would take a contract here or there to pay the bills, but longevity always remained a focus. Then last year, I decided to go back to the workplace full time. I needed a bit of a change of pace, and having a steady, well-paying job was also a very welcome development. For the first six months or so on the job, I pretty much set longevity entirely to the side. Eventually, it started creeping back into my head. I had actually started working on my first foray into shapeless before I took the job, and I started getting curious as to what the state of that branch was. I had identified the two major design flaws I had to work with, and I had an idea for a plan of how to address them. Here it is.<br />
<br />
I love <a href="https://www.postgresql.org/">PostgreSQL</a>. I think it's about the best relational database ever. One thing nice about Postgres is that it supports arrays and composite types. And I mean fully supports them, including indexing, full query support, etc. This would make a columnar back end particularly easy. Correcting each one of my major design flaws would be a major challenge - why not address them both at once, and save some hassle? On top of that, it would probably be easier to do this work in a greenfield back end, rather than migrate an existing back end. Implement it entirely with shapeless, and implement it entirely in columns, without any JSON. Once I have this under my belt, it will be much easier to go back and migrate the existing back ends.<br />
<br />
So this is what I plan to do next. Many things have changed over the past year or so, and longevity is now a hobby project, and no longer my primary focus. This is freeing in a lot of ways. It takes the pressure off. It also means development will go slowly, and developing the new back end will take some time. But I'm really excited about it! It's going to be a lot of fun.<br />
<br />
I'm also glad to be done with this little essay. It means the next time I have an hour to poke at longevity, I'll be able to dig right into the code!Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-61004035715217637622018-07-03T19:05:00.001-04:002018-07-03T19:05:09.467-04:00Longevity 0.27 - Stepping into ShapelessAfter over 8 months since it's previous release, <a href="https://github.com/longevityframework/longevity/releases/tag/0.27.0">longevity version 0.27</a> is out! This release is very exciting to me, as it represents <a href="http://longevityframework.org/">longevity</a>'s first steps into <a href="https://github.com/milessabin/shapeless">shapeless</a>. I was able to incorporate <span style="font-family: "courier new" , "courier" , monospace;">shapeless.Generics</span> into the domain model for all of the persistent objects, which will allow me to replace home-grown reflection based code with shapeless going forward.<br />
<br />
The externally facing changes are minor. I've replaced a home-grown test data generation tool with <a href="https://www.scalacheck.org/">ScalaCheck</a> and <a href="https://github.com/alexarchambault/scalacheck-shapeless">scalacheck-shapeless</a>. However, this minor change has already reaped benefits, as it replaces a particularly ugly part of the old longevity API - <a href="http://longevityframework.org/manual/testing/constraints.html">custom generation of test data</a> - with a much more elegant process of supplying the right implicit <span style="font-family: "courier new" , "courier" , monospace;">org.scalacheck.Arbitrary</span>.<br />
<br />
This minor feature is really a proof of concept that I would be able to replace my homegrown reflective code with shapeless features. I feel confident that I can migrate the entire architecture over. Although of course, it will take some time. I will write more soon on how I plan to move forward from here. Stay tuned!<br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-15939921430112740662017-10-26T09:23:00.000-04:002017-10-26T09:23:37.166-04:00Longevity 0.26 - Migrations Framework<a href="http://longevityframework.org/">Longevity</a> release 0.26 is out, and features the initial version of a schema migrations framework. You might ask, "Why would longevity - a persistence framework for Scala and NoSQL - require its own migrations framework? Couldn't it use any number of existing tools for doing schema migrations?"<br />
<br />
Because longevity handles persistence for you, allowing you to write you application in terms of your domain model, the traditional approach to migrating schema with database scripts does not always work out. At present, every back end stores your persistent objects in JSON. Back ends such as SQLite and Cassandra store the JSON in regular text columns. MongoDB offers update commands that allow you to modify your BSON documents dynamically, so a more traditional approach to schema migrations may work for you. It’s also possible that we will be adding column-based back ends for <a href="https://github.com/longevityframework/longevity/issues/47">SQLite</a> and <a href="https://github.com/longevityframework/longevity/issues/46">Cassandra</a> in the future, which will also be amenable to more traditional approaches.<br />
<br />
But even if a database-oriented tool such as <a href="https://flywaydb.org/">FlywayDB</a> works for you, we still want to provide you with an option that raises schema migrations up to the domain model abstraction layer. With longevity migrations, you define your domain model evolution in terms of Scala functions that map from the old version of the domain to the new version. So no database scripts are needed.<br />
<br />
I'm excited about this release because the lack of tools to help migrate a schema was, in my mind, the last major showstopper for using longevity in a production environment. Domain models do evolve, and we need to be able to bring existing data along with the changes. It's a pretty bare bones implementation of a migrations framework, but it's entirely functional, and I have a <a href="https://github.com/longevityframework/longevity/wiki/Longevity-Migrations---Ideas-for-Future-Directions">long list of ideas</a> on ways to improve it.<br />
<br />
My favorite feature by far on the wish list, I've been calling soft-stop migrations. If our migrations are defined in terms of Scala, that gives us the opportunity to keep both versions of the schema alive in the database simultaneously. While we migrate from one version to another, applications running the old version can use those Scala functions to keep the new version up to date with ongoing changes. This way, the application can continue running throughout the process of a schema migration. This kind of thing is very difficult to do when your migration scripts are written in a database query language. And limiting downtime is highly desirable. It would also be relatively easy to implement, and I'm tempted to do it. But now that I have a basic schema migrations tool in place, I'd like to focus on replacing home-grown reflective code with <a href="https://github.com/milessabin/shapeless">shapeless</a> for a while.<br />
<br />
Check out the <a href="http://longevityframework.org/manual/migrations/">chapter on migrations</a> in the user manual to get a feel for how it works.<br />
<br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-90507296720325205872017-09-06T16:59:00.000-04:002017-09-06T16:59:28.276-04:00longevity IDEA pluginI'm so excited to announce the <a href="https://github.com/longevityframework/longevity-idea-plugin">longevity IDEA plugin</a>, written by <a href="https://github.com/mardo">Mardo Del Cid</a>, that helps <a href="https://www.jetbrains.com/idea/">IntelliJ</a> understand the longevity macro annotations! If you are using IDEA with your longevity project, installing this plugin will remove your red squiggles! It's in the <a href="https://plugins.jetbrains.com/plugin/9896-longevity">JetBrains plugins repository</a>, so you can install it just like you would any other plugin for IntelliJ. Just search for “longevity” under Plugins in the <a href="https://www.jetbrains.com/help/idea/settings-preferences-dialog.html">Settings / Preferences Dialog</a>.<br />
<br />
I haven't been using IDEA much at all these last couple years, so I hadn't really realized that IDEA was showing false errors for projects using longevity. I'm grateful to have a user that discovered and reported the problem. I wonder how many people saw this and never even mentioned it! But the best part is that Mardo didn't just tell me about it, he went ahead and fixed the problem as well! I really, really appreciate it. Thank you.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-19044661335107960452017-08-02T22:55:00.001-04:002017-08-02T22:55:20.544-04:00Summer UpdateHi there!<br />
<br />
I'll be taking a month-long vacation starting this weekend (family road trip), and I wanted to drop a quick line before I head off, and give a status update on <a href="http://longevityframework.org/">longevity</a>.<br />
<br />
First off, I made a <a href="https://www.youtube.com/watch?v=AJMETrjSzzY&t=5s">video tutorial</a> last week that's pretty nice. It's about 70 minutes, but it's probably a comfortable watch for most people at 1.5x speed.<br />
<br />
I've been playing around with early steps in migrating to <a href="https://github.com/milessabin/shapeless">shapeless</a> on a <a href="https://github.com/longevityframework/longevity/tree/feat/arbitrary">feature branch</a>. The first bite I broke off for this is to rewrite my test data generation tools using <span style="font-family: Courier New, Courier, monospace;">Arbitrary</span> from <a href="http://www.scalacheck.org/">ScalaCheck</a>. It's not finished yet, but I got through the most worrisome part, which was, how am I going to generate all the implicit stuff used by shapeless libraries? In this case, I need a bunch of implicits to generate an <span style="font-family: Courier New, Courier, monospace;">Arbitrary[P]</span> using <a href="https://github.com/alexarchambault/scalacheck-shapeless">scalacheck-shapeless</a>. The worrisome part is that I have to generate the class for a user-provided type <span style="font-family: Courier New, Courier, monospace;">P</span>. This is problematic in general, because in order to get things like a <span style="font-family: Courier New, Courier, monospace;">Generic[P]</span>, you have to have the actual type <span style="font-family: Courier New, Courier, monospace;">P</span> right there in your hands. Nothing else will do - not a <span style="font-family: Courier New, Courier, monospace;">TypeTag</span>, nothing else along those lines. Thankfully, I already have macros that produce scaffolding around the type <span style="font-family: Courier New, Courier, monospace;">P</span>, so I can generate the code to produce the <span style="font-family: Courier New, Courier, monospace;">Arbitrary[P]</span>, and it ends up getting inserted right there in the user code, where <span style="font-family: Courier New, Courier, monospace;">P</span> is well known. I'm sure happy about that because it means the major worry I had about implementing things with shapeless is now solved.<br />
<br />
So that's on a branch, and I put that aside because I decided I really need to start focusing in on providing users a schema migration tool. It's really the last "absolutely must have" feature for longevity, and while working on making longevity more functional is very enticing, I need to keep my priorities straight. The last two functionalization tasks - <a href="https://scabl.blogspot.com/2017/06/longevity-release-023-use-type-classes.html">the <span style="font-family: Courier New, Courier, monospace;">ModelEv</span>, <span style="font-family: Courier New, Courier, monospace;">PEv</span>, and <span style="font-family: Courier New, Courier, monospace;">Key</span> type classes</a>, and the <a href="https://scabl.blogspot.com/2017/07/longevity-goes-finally-tagless.html">finally tagless</a>, were both super important, but most importantly, user-facing changes. Most of the rest of the functionalization work I want to do is more internal, and shouldn't affect the user API too much. So I can definitely de-prioritize it. Only thing is, I want to support options in queries, and I've sort of been delaying that until after shapeless is in place. I could actually do it before then, but it just isn't the most efficient course, as I would have to rewrite most of it in shapeless.<br />
<br />
So when I come back in September, I'll definitely be focusing on schema migration tool first. Once I have a rudimentary working tool out, I'll probably start splitting my time between that and migrating to shapeless.<br />
<br />
Have a great August everybody!Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-87308566059194378602017-07-06T18:53:00.000-04:002017-07-08T07:38:57.684-04:00Longevity Goes Finally Tagless<a href="http://longevityframework.org/">Longevity</a> - your persistence framework for Scala and NoSQL - has always sported a future-based API. But now in <a href="https://github.com/longevityframework/longevity/releases/tag/0.24.0">release 0.24</a>, longevity has gone <a href="https://pchiusano.github.io/2014-05-20/scala-gadts.html">finally tagless</a>, replacing the hardcoded references to <span style="font-family: "courier new" , "courier" , monospace;">scala.concurrent.Future</span> with a generic effect <span style="font-family: "courier new" , "courier" , monospace;">F</span>. We currently support three effects: Scala futures, the <a href="https://github.com/typelevel/cats-effect">cats-effect</a> IO monad, and an old-fashioned blocking API for the purists out there. We plan to support a variety of <span style="font-family: "courier new" , "courier" , monospace;">Task</span> monads as effects in the near future, and you can always write your own effect as well.<br />
<br />
For more information on longevity effects, see the <a href="http://longevityframework.org/manual/context/effects.html">user manual</a>.<br />
<br />
For examples on how longevity works with different effects, check out this <a href="https://github.com/longevityframework/demo/tree/master/src/main/scala">demo code</a>.<br />
<br />
This release is exciting for me for many reasons. Perhaps most importantly, longevity now sports a truly functional API with support for IO monads. But the flexibility of the finally tagless approach is truly freeing. We can satisfy the purist functional programmers, and continue to support people who want to do reactive programming with futures, with a minimum of cognitive overhead. It's also exciting to me because it's a major step on my personal path to becoming a better functional programmer.<br />
<br />
But the best part for me is just how beautiful the <a href="http://longevityframework.org/api/longevity/persistence/Repo.html">repository API</a> has become. Just look at it. In a sense, this is how this API was always meant to be. Perfectly intuitive, fully typesafe, just the right level of abstraction without a hint of leaks. I couldn't imagine doing persistence in Scala any other way.<br />
<a name='more'></a><br />
So what else comes in this release? I've removed a couple of superfluous features from the API that were not really useful and were clogging up my API. I've also started a major shift with the emblem project. This project is now officially dead. Most of what emblem does, you'd be better off using <a href="https://github.com/milessabin/shapeless">shapeless</a>. Myself, I've moved most of the old emblem code internal to longevity, and plan to replace it with shapeless over time.<br />
<br />
I've extracted the useful parts of emblem into their own library named <a href="https://github.com/longevityframework/typekey">typekey</a>. This library is really cool and you should check it out. You can do similar things with shapeless Poly, but <a href="https://longevityframework.github.io/typekey/typeboundmaps.html">type bound maps</a> have a much prettier API, and more likely than not, have much better performance characteristics. Having heard Miles Sabin steer people away from Poly now and then, I think type bound maps are definitely worth a look if you are looking to solve a problem with something like Poly.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-75313476516404971882017-06-12T11:12:00.004-04:002017-06-12T11:13:58.367-04:00Longevity Release 0.23 - Use Type Classes to Improve Type Safety of Persistent APIThis <a href="http://longevityframework.org/">longevity</a> release has been a long time in the making. The main improvement here, and the one that has taken by far the most effort, is instituting the use of type classes to provide type safety for the persistence API. I've done a technical writeup on this work in my <a href="http://scabl.blogspot.com/2017/06/an-interesting-use-case-for-type-classes.html">previous blog post</a>. It's actually pretty interesting because I've never seen type classes quite this way before, so I think it's worth the read. The TLDR is that the repository API is now fully type safe, and the last instance of forcing users to inherit from a longevity trait has been removed.<br />
<br />
Here are some of the other major improvements in this release:<br />
<a name='more'></a><br />
<h3>
Simplify API by merging Repo and RepoPool</h3>
<br />
Previously, the persistence API was divided into one repository - the <span style="font-family: "courier new" , "courier" , monospace;">Repo</span> - for every kind of persistent object you wanted to persist. Some top-level operations, such as creating schema and closing session, were put into the top-level container for these repositories - the <span style="font-family: "courier new" , "courier" , monospace;">RepoPool</span>. I've simplified these things by merging the API into a single <span style="font-family: "courier new" , "courier" , monospace;">Repo</span> object that handles persistence for all your types.<br />
<br />
Having a separate repository for each persistent type is a holdover from the days when a JVM persistence layer was just a wrapper around the underlying database driver. You either get a thin layer over things like database queries, or some intermediate representation that has some aspects of database modeling, and some aspects of domain modeling, such as <a href="https://en.wikipedia.org/wiki/Java_Persistence_Query_Language">JPQL</a>. As such query languages are steeped in the language of your database model, they need to remain part of the persistence layer. Because every persistent type has its own set of queries, there is a need to host these queries in a repository for that persistent type. Even in something as old-fashioned as Java/JPA, we can abstract away CRUD operations, and largely inherit them from a common superclass. But queries are a bit more complicated than that.<br />
<br />
Longevity takes a different approach. It provides an abstraction layer at just the right point - between the database model and the domain model. Database model concepts are easily abstracted into domain model terms. For instance, a database key is a way to ensure that a domain object is unique over certain attributes, and can be retrieve quickly over those attributes. We can express concepts like keys and queries without having to blur or leak between layers of abstraction, and talk about keys and queries in pure domain model terms. Once your queries become part of the domain model, the need for having a separate repository for each persistent type disappears.<br />
<br />
<h3>
Start Signing Release Tags</h3>
<br />
I started following some of the useful advice by Daniel Spiewak about <a href="https://gist.github.com/djspiewak/ec8d4f3fa37d94cbbfdb39c42d9e3a40">how to use encryption as an open-source maintainer</a>. I was already signing my release jars, but Daniel showed me how to sign my release tags as well. Here is a screenshot of <a href="https://github.com/longevityframework/longevity/releases/tag/0.23.0">my first signed Git tag</a>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-fiSRcqvYCwk/WT6avHaDtsI/AAAAAAAAfuQ/QNe9Sl8WMXYdTQ5QzbDbD1ZWWdFPZ02VQCLcB/s1600/Screen%2BShot%2B2017-06-12%2Bat%2B08.43.54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="608" data-original-width="1600" height="150" src="https://2.bp.blogspot.com/-fiSRcqvYCwk/WT6avHaDtsI/AAAAAAAAfuQ/QNe9Sl8WMXYdTQ5QzbDbD1ZWWdFPZ02VQCLcB/s400/Screen%2BShot%2B2017-06-12%2Bat%2B08.43.54.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
(I've really got to update my icon from the Lorax at some point. It's been on GitHub and elsewhere for years. I'll always continue to speak for the trees, but that is not particularly relevant to the Scala community.)</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
I've also set up <a href="https://keybase.io/johnsullivanmscs">an account on Keybase</a>, as Daniel suggested.</div>
<div class="separator" style="clear: both;">
<br /></div>
<h3>
Start Using sbt-site and sbt-ghpages for Website Generation</h3>
<br />
I've improved my website documentation process a good deal with the use of two excellent SBT plugins: <a href="https://github.com/sbt/sbt-site">sbt-site</a> and <a href="https://github.com/sbt/sbt-ghpages">sbt-ghpages</a>. If you use <a href="https://pages.github.com/">GitHub Pages</a> to build your website or documentation, I highly recommend you check these out.<br />
<br />
I initially set out to use <a href="https://github.com/tpolecat/tut">tut</a> as a tool to check that the code examples in my manual actually compiled. I wanted to replace a clumsy manual process of maintaining a copy of these code examples in my unit test suite, and copy/pasting or hand modifying to keep them in sync. It was a pretty ugly process; both time consuming and error prone. Unfortunately, tut didn't work out for me, as it is based on the Scala REPL, and the great majority of my code examples do not work in the Scala REPL without going into :paste mode (which tut <a href="https://github.com/tpolecat/tut/issues/62">doesn't currently support</a>.) I did take some time to streamline the process a little bit, making the mapping between the code samples in my manual and their versions in the unit test suite much more direct.<br />
<br />
<h3>
What's Next</h3>
<div>
<br />
Next step to tackle is another API improvement. I want to fix the longevity API to not specialize on <span style="font-family: "courier new" , "courier" , monospace;">scala.concurrent.Future</span>, but instead use a <a href="https://pchiusano.github.io/2014-05-20/scala-gadts.html">tagless final</a> approach to support whatever kind of effect you like, be it <span style="font-family: "courier new" , "courier" , monospace;">Future</span> or something more functional like an <a href="http://typelevel.org/blog/2017/05/02/io-monad-for-cats.html">IO monad for cats</a>. I'm looking to grow as a functional programmer, and to grow longevity as a functional library, and I decided the best place to start was the user-facing API. This 0.23 release and the tagless final stuff are the main thrusts there. I do want to keep the longevity API completely accessible to, say, recent expats from Java. My feeling with the type class work in this release is that, while it does complicate the method signatures in the Scaladocs, the user code looks more or less exactly the same. So the barrier of entry raises slightly for the Java expat in reading the API docs, but I am hoping that is offset by having a clear and complete user manual that explains what is going on.</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-17909780689303538222017-06-09T12:43:00.000-04:002017-06-12T11:15:45.041-04:00An Interesting Use for Type Classes in Scala<span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";">This is a technical writeup of the major new feature in the <a href="http://scabl.blogspot.com/2017/06/longevity-release-023-use-type-classes.html">longevity 0.23 release</a>.</span><br />
<span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";"><br /></span>
<span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";">There are more than enough writeups on using type classes in Scala already. I normally wouldn't bother to write about it, but I will now, for three reasons. First, the usage of type classes in </span><a href="http://longevityframework.org/" style="box-sizing: border-box; color: #0366d6; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; text-decoration-line: none;">longevity</a><span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";"> seems unique to me; I haven't seen type classes used quite this way before. Second, I want to write this up as documentation for any future contributors to the longevity project. Third, I'd really like to get your feedback into what I've done here, and hear your advice about how I might further improve on things.</span>
<br />
<div style="box-sizing: border-box; margin-bottom: 16px;">
<span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";"></span></div>
<a name='more'></a><h2>
The Old Way</h2>
<div style="box-sizing: border-box; margin-bottom: 16px;">
The best way to get started is to look at how things in longevity worked <em style="box-sizing: border-box;">before</em> I introduced the type classes. I'm going to gloss over a lot of stuff here, and even present code anachronistically, (i.e., present code features that never actually lived together in the same commit), so as to keep things simple and avoid bringing up any issues that are not necessary for understanding the case at hand.<br />
<br />
<h3>
Domain Model Elements</h3>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
In longevity, we talk a lot about our <em style="box-sizing: border-box;">domain model</em> - these are all the Scala traits and case classes that represent the data we want to persist. We divide these into three categories: <em style="box-sizing: border-box;">persistent objects</em>, or the root objects we want to persist in a table or collection; <em style="box-sizing: border-box;">components</em>, or elements embedded in our persistent obejcts, and <em style="box-sizing: border-box;">key values</em>, or values used to look up our persistent objects. In the companion objects to our persistent classes, we also describe the details of the keys that we can use to look them up by key value. We construct all these types in the same package or subpackages, and annotate them with corresponding longevity annotations. Before type classes, this might have looked something like this:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">package</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">myModel</span>
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">import</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">longevity.model.annotations.</span><span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">_</span>
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">@</span>persistent
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">case</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">class</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">User</span>(<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">username</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Username</span>, <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">email</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Email</span>, <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">fullname</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Fullname</span>)
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">User</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">keySet</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Set</span>(primaryKey(props.username), key(props.email))
}
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">@</span>keyVal[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">User</span>]
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">case</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">class</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Username</span>(<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">username</span>: <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">String</span>)
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">@</span>keyVal[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">User</span>]
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">case</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">class</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Email</span>(<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">email</span>: <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">String</span>)
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">@</span>component
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">case</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">class</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Fullname</span>(<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">last</span>: <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">String</span>, <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">first</span>: <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">String</span>)</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
That probably looks quite intuitive to you, aside from the contents of the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">User</code> companion object. The <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@persistent</code> annotation extended the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">User</code> companion object with <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">longevity.model.PType[User]</code>, which contains various information about our persistent type that we need in order to do persistence, including creating the database schema, looking up users by key values, and looking up users by query. The <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType</code> has an abstract <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">val keySet: Set[Key[User, _]]</code> that we need to fill in. It also provides methods such as <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">key</code> and <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">primaryKey</code> to build the keys. Finally, the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@persistent</code> object creates a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">object props</code>inside <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">object User</code>, that contains <em style="box-sizing: border-box;">properties</em>, which we can use to reflectively describe the fields of a user. As you can see above, we made use of these properties to define our two keys.<br />
<br />
<h3>
Domain Model and Longevity Context</h3>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Once we get this far, we need to collect all our elements into what used to be called a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">longevity.model.DomainModel</code>. We would do this like so:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">import</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">longevity.model.annotations.</span><span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">domainModel</span>
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">package object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">myModel</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">@</span>domainModel <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
The <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@domainModel</code> annotation would scan the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">myModel</code> package, and all its subpackages, to gather up artifacts describing all the persistents, components, and key values in our model. (What it actually scans for are companion objects that were made into <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PTypes</code>, <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">CTypes</code>, and <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">KVTypes</code> by the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@persistent</code>, <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@component</code>, and <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@keyVal</code> annotation macros.) It would make <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">MyDomainModel</code> extend <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">longevity.model.DomainModel</code>, and pass in all the artifacts to the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">DomainModel</code> constructor. We then passed on our domain model to a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">longevity.context.LongevityContext</code>:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">import</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">longevity.context.</span><span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">LongevityContext</span>
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">myContext</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">LongevityContext</span>(<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>)</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
The longevity context builds a handful of tools that you can use to work with your model. The most important of these is the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo</code>, which provides a complete set of persistence operations:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">repo</span><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> longevity.persistence.<span class="pl-en" style="box-sizing: border-box; color: #795da3;">Repo</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> myContext.repo</pre>
</div>
<h3>
The API Ugly</h3>
<br />
<div style="box-sizing: border-box; margin-bottom: 16px;">
Now, I'd like to focus in on two repository methods that used to be annoyingly un-typesafe. Most of the other <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo</code> methods displayed one of these two problems in an analogous way. I spent a lot of thought, time, and effort trying to improve this situation, but none of the standard OO techniques I am familiar with were any help. First, here's the method for inserting a new persistent object in the database:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-en" style="box-sizing: border-box; color: #795da3;">Repo</span>.create[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>](<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">p</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>)(<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">c</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">ExecutionContext</span>)<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Future</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">PState</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>]]</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
One thing that glares at me right now is the hardcoding of Scala futures for an effect. My next major push will be to tackle this, allowing for other effects. Right now, I want to focus on the fact that <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">P</code> is <span style="box-sizing: border-box; font-weight: 600;">completely untyped</span>. Due to one of those anachronisms I mentioned before, it never looked quite so bad as it does above, because there was an extra level of indirection in the API that I have also removed. But even if it was hidden a bit, it was just as bad. This is really ugly! If you pass in any object that is not known by your domain model to be a persistent object, then you will get no compiler error, and some kind of runtime exception here.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
It used to be that I had the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@persistent</code> annotation cause the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">User</code> class to extend a no-longer existent <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">longevity.model.Persistent</code> trait. But I decided it was ugly and untenable to force user classes to extend a library trait, even when the trait is completely empty. And while forcing <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">P <: Persistent</code> provided some type safety, it did not actually make the method typesafe, because you could still pass in a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Persistent</code> that was not known by the domain model!</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Let's look at the second repository method that was also causing me grief:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-en" style="box-sizing: border-box; color: #795da3;">Repo</span>.retrieve[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;"><</span><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">KeyVal</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span>]](<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">v</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span>)(<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">c</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">ExecutionContext</span>)<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Future</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">Option</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">PState</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>]]]</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Here, I faced as similar problem, but chose the stay with my original solution: The <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@keyVal</code>annotation would mark the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Username</code> class by making it extend <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">longevity.model.KeyVal[User, Username]</code>. So here, I was still forcing user classes to extend a library class. And it <em style="box-sizing: border-box;">still</em> did not actually provide type safety. Again, <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">P</code> might not have been a persistent class that the domain model knew about. And <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">V <: KeyVal[P, V]</code> might not have been associated with an actual key that the domain model knew about either. The reason why I chose to leave <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">KeyVal</code> in when I pulled <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Persistent</code>, is that I had made the following error myself more than once:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">fopUser</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> repo.retrieve[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">User</span>](user) <span class="pl-c" style="box-sizing: border-box; color: #969896;"><span class="pl-c" style="box-sizing: border-box;">//</span> oops I meant username, not user</span></pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
So I have to note I am glossing over one point here: The <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo.retrieve</code> API is actually broken into two parts so as to not force longevity users from explicating the key value type when using retrieve. This is why the above line of code only has one type parameter, but there are two in the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo.retrieve</code> API as I wrote it a bit further up.</div>
<h2 style="box-sizing: border-box; margin-bottom: 16px;">
<span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";">A "Phantom" Type for Domain Models</span></h2>
<div style="box-sizing: border-box; margin-bottom: 16px;">
After some more exposure to type classes, and a good deal of thought, I finally came up with a way that I could overcome these problems. But it's going to take a few steps to get there. The first realization is that I am going to need a type to represent my domain model in type parameter positions in a number of signatures, so the old <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@domainModel object MyDomainModel</code> wasn't going to do it any more. So I changed this <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">object MyDomainModel</code> into a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">trait MyDomainModel</code>:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">package</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">myModel</span>
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">import</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">longevity.model.annotations.</span><span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">domainModel</span>
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">@</span>domainModel <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">trait</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span></pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
This annotation no longer augments the thing it annotates by making it a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">longevity.model.DomainModel</code>. Instead, it sticks a couple of things into the companion object for <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">MyDomainModel</code>. One of those things is what I used to call the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">DomainModel</code> - the thing that contains all the information about the classes in the model the longevity user wants to persist. I renamed this from <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">DomainModel</code> to <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code> because, type classes. <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">DomainModel</code> wasn't really appropriate any more, since this is now something that describes the model at the type level, instead of being an object that knew all the details of the domain model.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
I make the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code> take the model as a type parameter, and make it implicit, like so:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">modelType</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">extends</span> <span class="pl-e" style="box-sizing: border-box; color: #795da3;">longevity.model.ModelType</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDOmainModel</span>](<span class="pl-c" style="box-sizing: border-box; color: #969896;"><span class="pl-c" style="box-sizing: border-box;">/*</span> ... <span class="pl-c" style="box-sizing: border-box;">*/</span></span>)
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
The <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">/* ... */</code> comment above is filled in with lists of persistent types, component types, and key value types that the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@domainModel</code> macro found while scanning the package and sub-packages. The <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">LongevityContext</code> factory method has changed from this (simplified):</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">LongevityContext</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">def</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">apply</span>(<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">domainModel</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">DomainModel</span>)<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">LongevityContext</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">???</span>
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
To this:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">LongevityContext</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">def</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">apply</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">M</span>](<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">modelType</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">ModelType</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">M</span>])<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">LongevityContext</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">M</span>] <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">???</span>
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
And when we call it like so:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">context</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">LongevityContext</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>]()</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
The implicit <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType[M]</code> is easily found by the compiler in the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">MyDomainModel</code> companion object.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
One interesting thing here is that <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">MyDomainModel</code> is a sort of "phantom" type, as it is never actually instantiated. But it's quite different from what is normally called a phantom type in Scala, as described in many places, including <a href="https://blog.codecentric.de/en/2016/02/phantom-types-scala/" style="box-sizing: border-box; color: #0366d6; text-decoration-line: none;">here</a>. Is <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">MyDomainModel</code> a phantom type? I don't think so. So what do we call it? I've just been calling it a marker type.</div>
<h2 style="box-sizing: border-box; margin-bottom: 16px;">
<span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";">Evidence that a the Persistent is Part of the Domain Model</span></h2>
<div style="box-sizing: border-box; margin-bottom: 16px;">
There's a second implicit object put into the companion object by the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@domainModel</code>annotation - the model evidence. The companion object actually looks something like this:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">modelType</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">extends</span> <span class="pl-e" style="box-sizing: border-box; color: #795da3;">longevity.model.ModelType</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>](<span class="pl-c" style="box-sizing: border-box; color: #969896;"><span class="pl-c" style="box-sizing: border-box;">/*</span> ... <span class="pl-c" style="box-sizing: border-box;">*/</span></span>)
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">private</span>[myModel] <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">modelEv</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">extends</span> <span class="pl-e" style="box-sizing: border-box; color: #795da3;">longevity.model.ModelEv</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>]
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
The "ev" in <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv</code> here is short for "evidence". You might wonder, why not use just a single type class here, merging <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code> and <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv</code>? There are two reasons for this. The first is initialization order - the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv</code> has to be used to construct the elements that are supplied to the constructor of the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code>. The second is, it is important that the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv</code> is package private, as we will see. It's also important that the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code> is not, because it needs to be found to construct the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">LongevityContext</code>, as we saw above, and the context typically lives in another package.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
We discussed earlier how the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@persistent</code> annotation doctors up the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">User</code> companion object as a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType[User]</code>, or persistent type. In the new API, the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType</code> takes a second type parameter for the domain model, and the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType</code> constructor requires an implicit <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv</code>. Something like this:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">abstract</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">class</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">PType</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">M</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">ModelEv</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>] {
<span class="pl-c" style="box-sizing: border-box; color: #969896;"><span class="pl-c" style="box-sizing: border-box;">//</span> ...</span>
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Because the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv</code> is private to the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">myModel</code> package, <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PTypes</code> can only be constructed in the same package. This is exactly where the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PTypes</code> are found by the macro that scans packages to find <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PTypes</code>. So the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PTypes</code> that make it into the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code> are now the exactly of <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PTypes</code> that can actually be constructed. (With a couple of caveats - see the "Type Holes that Still Exist" section below.)</div>
<h2 style="box-sizing: border-box; margin-bottom: 16px;">
<span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";">Persistent Evidence</span></h2>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Next step is to create another kind of evidence - this time for the persistent class. It's called <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">longevity.model.PEv[M, P]</code>, and it is found as an implicit val within the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType[M, P]</code>. The <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PEv</code> constructor is private to package <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">longevity.model</code>, so users are unable to subvert the type system by creating their own persistent evidence. I next modify the signature for <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo.create</code> as follows:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-en" style="box-sizing: border-box; color: #795da3;">Repo</span>.create[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>](<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">p</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>)(<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">pEv</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">PEv</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">M</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>], <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">c</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">ExecutionContext</span>)<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Future</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">PState</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>]]</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
This causes a compiler error if the evidence is not found. If your persistent class is actually part of the model, then its companion object is a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType[M, P]</code>, and because of this, it has an implicit <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PEv[M, P]</code> inside of it. And the Scala compiler will find it. We've managed to make the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo.create</code> method typesafe using type classes, where standard OO approaches failed us.</div>
<h2 style="box-sizing: border-box; margin-bottom: 16px;">
<span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";">Evidence for a Key Value</span></h2>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Let's take a look at the old signature for <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo.retrieve</code> again:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-en" style="box-sizing: border-box; color: #795da3;">Repo</span>.retrieve[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;"><</span><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">KeyVal</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span>]](<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">v</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span>)(<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">c</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">ExecutionContext</span>)<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Future</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">Option</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">PState</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>]]]</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Of course, the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">KeyVal[P, V]</code> has to go. We <span style="box-sizing: border-box; font-weight: 600;">do not</span> want to force longevity users to extend their classes with library traits, even if they are just marker traits. So what do we need to make this typesafe? For one, we need to know that <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">P</code> is part of the domain model, as before. But we also need to know that <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">V</code> is a key value for an actual key. The <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Key</code> itself, that lives within the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType</code>, is perfect evidence for this. But there's a problem: We used to create keys as anonymous members of a set, like so:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">User</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">keySet</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Set</span>(primaryKey(props.username), key(props.email))
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
These keys will never be found by the compiler via implicit resolution. So I changed the API so that the user has to write the following instead:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">User</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">usernameKey</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> primaryKey(props.username)
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">emailKey</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> key(props.email)
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
longevity still needs the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType.keySet</code> to do things like building out the database schema required to support the keys. But the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">keySet</code> is now private, and it is constructed by reflecting over the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType</code>, scanning for keys. (Unlike with the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">@domainModel</code> scanning, which uses compile-time reflection, I'm using runtime reflection here for now. I'll probably change that to compile-time reflection soon.)</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Now, keys can only be created using protected methods in the abstract class <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType</code>. So the keys that we can create for a persistent are exactly the keys that are found by the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code>. (Again, with caveats. See the "Type Holes" section below.) So we can use the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Keys</code> themselves as evidence for the key value type <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">V</code>. I rewrote the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo.retrieve</code>method to look like this:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-en" style="box-sizing: border-box; color: #795da3;">Repo</span>.retrieve[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span>](<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">v</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span>)(
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">pEv</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">PEv</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">M</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>],
<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">kvEv</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Key</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">M</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span>],
<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">c</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">ExecutionContext</span>)<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Future</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">Option</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">PState</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">P</span>]]]</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
As before, the implicit <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PEv</code> assures that the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code> actually knows <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">P</code> as a persistent. The implicit <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Key</code> assures that the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code> actually knows about a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Key</code> of type <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">V</code>. Once again, we've solved a type safety problem with type classes that we were unable to solve with OO techniques.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
(I'll mention again that I am glossing over a detail of the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo.retrieve</code> API that prevents longevity users from having to explicitly specify a value for the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">V</code> type parameter.)</div>
<h2 style="box-sizing: border-box; margin-bottom: 16px;">
<span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";">Type Holes that Still Exist</span></h2>
<div style="box-sizing: border-box; margin-bottom: 16px;">
This type system is not perfect. I have come up with four ways so far that the type system could be subverted by the user, bringing on the runtime exception. Let's take a look.<br />
<br />
<h3>
Forging Model Evidence</h3>
Suppose a user creates a persistent class, but they put it outside the domain model package. Code like this:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">@</span>persistent[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>]
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">case</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">class</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Foo</span>(<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">bar</span>: <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">String</span>, <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">baz</span>: <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">String</span>)</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Will create or augment the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Foo</code> companion object to look like this:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Foo</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">extends</span> <span class="pl-e" style="box-sizing: border-box; color: #795da3;">longevity.model.PType</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Foo</span>] {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">props</span> {
<span class="pl-c" style="box-sizing: border-box; color: #969896;"><span class="pl-c" style="box-sizing: border-box;">//</span> ...</span>
}
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
You will recall that the constructor for abstract class <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType[M, P]</code> takes an implicit argument of type <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv[M]</code>. If the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Foo</code> case class lives outside the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">myModel</code> package, the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv</code> inside the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">MyDomainModel</code> companion object will not be found, resulting in an "implicit not found" compiler error. A clumsy user might decide at this point to create a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv[MyDomainModel]</code> themselves, that is in the appropriate scope. Or they might even do something more crazy, like this:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">@</span>domainModel <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">trait</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span> {
<span class="pl-c" style="box-sizing: border-box; color: #969896;"><span class="pl-c" style="box-sizing: border-box;">//</span> remember, the following commented out line is put in by the @domainModel annotation:</span>
<span class="pl-c" style="box-sizing: border-box; color: #969896;"><span class="pl-c" style="box-sizing: border-box;">//</span> private[myModel] implicit object modelEv extends ModelEv[MyDomainModel]</span>
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">implicit</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">publicizedEvidence</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> modelEv
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
I'm not sure I can do anything to thwart these kinds of "workarounds". No matter what I do, there has to be some way for the user to create <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEvs</code> manually. (Remember that Scala macros always have to expand into compiling code, and so can always be rewritten by hand.) This will necessarily lead to opportunities for subversion of the approach to type safety described here.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
I've spelt out the situation in the manual and the Scaladocs for <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv</code>, and I'm hoping that's good enough.<br />
<br />
<h3>
The PType that Couldn't Be Found</h3>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
It's also possible to create a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType</code> that lives in the right package, but still cannot be found by the package scanning macro. This way, the user would have evidence for a persistent class that the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code> does not know about. Here's one way:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">import</span> <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">longevity.model.annotations.</span><span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">persistent</span>
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">package object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">myModel</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">trait</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Unscanned</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">@</span>persistent[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>] <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">case</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">class</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Foo</span>(<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">bar</span>: <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">String</span>, <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">baz</span>: <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">String</span>)
}
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
For better or worse, even if my package scanning found the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType[MyDomainModel, Foo]</code>generated here, it's going to create other problems down the line. The reflective techniques used by longevity to determine the structure of a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Foo</code> depend on the case class not being an inner class. I do plan on replacing those reflective techniques with a shapeless approach, and I'm not sure now if shapeless is going to be able to handle something like this either.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Even if migrating to shapeless makes reflecting over the shape of a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Foo</code> like this possible, it's still not going to help. Consider the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo.retrieve</code> method we have been discussing. It needs to create a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">P</code> from a <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">V</code>. Where is it going to find an <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Unscanned</code> to build the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Foo</code>from? Shall we require that the key value class <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">V</code> is declared inside the same <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Unscanned</code>trait, so that I can build the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Foo</code> from the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Unscanned</code> that the key value comes from? This is getting needlessly complicated. I'm not dead set against at making this kind of thing work, but at the moment, it seems far beyond the call of duty for a persistence API.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
Quick note to say that the manual is <a href="http://longevityframework.org/manual/model/limitations.html" style="box-sizing: border-box; color: #0366d6; text-decoration-line: none;">pretty explicit</a> about the fact that this kind of thing won't work.<br />
<br />
<h3>
Exposing PType.key</h3>
We depend on the keys for a persistent being found by reflective methods within the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType</code>for that persistent. This is mostly guaranteed by the fact that the only way you can create keys is via protected methods in the abstract class <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PType</code>. But a user could easily work around this, for instance:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">@</span>persistent[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>] <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">case</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">class</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Foo</span>(<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">bar</span>: <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">String</span>, <span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">baz</span>: <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">String</span>)
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Foo</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">def</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">exposedKey</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">KVEv</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Foo</span>, <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">?</span>]](
<span class="pl-v" style="box-sizing: border-box; color: #ed6a43;">keyValProp</span>: <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Prop</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">Foo</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span>])<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Key</span>[<span class="pl-en" style="box-sizing: border-box; color: #795da3;">MyDomainModel</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Foo</span>, <span class="pl-en" style="box-sizing: border-box; color: #795da3;">V</span>] <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> key(keyValProp)
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
The details of the signature for <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Foo.exposedKey</code> do not matter - only the fact that it exactly replicates the signature for <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">key</code>. And <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">exposedKey</code> can be called from absolutely anywhere, creating keys that the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code> will never be aware of. These keys could then be used as evidence in the call to <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Repo.retrieve</code>, causing havoc. Maybe a database error percolating up about some column that doesn't exist.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
I consider an approach like this employed by a user to be outright malicious, and I don't feel obligated to do anything about it.<br />
<br />
<h3>
The Key that Couldn't Be Found</h3>
Here's another way to produce a key that the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code> doesn't know about:</div>
<div class="highlight highlight-source-scala" style="box-sizing: border-box; margin-bottom: 16px;">
<pre style="background-color: #f6f8fa; border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">object</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Foo</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">class</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Unscanned</span> {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">barKey</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> key(props.bar)
}
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">val</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">unscanned</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">new</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Unscanned</span>
}</pre>
</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
I can almost imagine a user doing something like this without knowing that they were actually subverting the type system. Maybe if I make my reflective scanning more robust, I could catch cases like this?<br />
<br />
<h3>
Closing Thoughts on Type Holes</h3>
In general, the holes in the type system that I've managed to come up with all seem pretty extreme, edgy cases, and I'm not losing a lot of sleep over them. The thing that bothers me the most here is that there are so many of them. I can sort of ease my mind by imagining the things I might pull off if I, for instance, started writing code in my own project in package <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0.2em 0px;">scala.collection</code>. There are over a hundred <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0.2em 0px;">private[collection]</code> thingies I might start to mess around with.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
All the same, I still wonder if there is a better way to approach all of this? Is there some way I could make all this not just typesafe for the responsible user, but typesafe iron clad?</div>
<h2 style="box-sizing: border-box; margin-bottom: 16px;">
<span style="font-family: , "blinkmacsystemfont" , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";">What Makes this Usage of Type Classes Unique</span></h2>
<div style="box-sizing: border-box; margin-bottom: 16px;">
In most uses of type classes that I have seen, the type class provides extra functionality for dealing with your types. Like the way <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Seq.sortBy</code> and <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Seq.sorted</code> take implicit <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">scala.math.Ordering</code> arguments. We're doing something quite different here. The type classes <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelEv</code>, <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">PEv</code>, and <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">Key</code> are all being used as evidence that model elements are built out in the correct locations, so that they can be found by reflective scanning techniques.</div>
<div style="box-sizing: border-box; margin-bottom: 16px;">
I suppose reflective scanning techniques themselves are a little oddball in Scala, but I can't think of any other sensible way to gather up the elements of your domain model. I've often considered that putting them into some kind of cake-like structure might help, but I am assuming that users do not want to have to declare the types they want to persist in some kind of cake. And while I've never dwelled on it for too long, I've also never figured out how a cake would prevent the need to go around collecting the persistent element types. A while back, longevity users were actually forced to list out all of their domain model elements when constructing the <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">DomainModel</code> (renamed to <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; margin: 0px; padding: 0.2em 0px;">ModelType</code>, as described here). But this was not satisfactory. It's an error-prone case of asking users to repeat themselves.</div>
<div style="box-sizing: border-box;">
Can you think of a way I might pull this off without resorting to reflective scanning, and without forcing the user to manually list out their model types in some kind of collection?</div>
Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-5659866753486893848.post-45443649489894198772017-03-04T17:26:00.000-05:002017-03-04T17:26:34.537-05:00Longevity now has Better Support for Alternative JDBC Back Ends<a href="http://longevityframework.org/">Longevity</a> release 0.21 features a new back end called <span style="font-family: "courier new" , "courier" , monospace;">JDBC</span>. It should work in a lot of situations where using <span style="font-family: "courier new" , "courier" , monospace;">SQLite</span> back end with a non-SQLite driver would fail. There is also a much easier development path for creating your own back end, should the generic JDBC back end not be sufficient.<br />
<a name='more'></a><br />
In the show notes for the <a href="http://scabl.blogspot.com/2017/01/new-sqlite-back-end-for-longevity.html">SQLite release</a>, I suggested that people could probably use this back end with other JDBC drivers, since it was mostly vanilla JDBC. But in the back of my mind, I sensed there might be some problems with this. There are a couple of places where <span style="font-family: "courier new" , "courier" , monospace;">org.sqlite.SQLiteException</span> is caught. While these might never get hit, I've since realized that the mere reference to <span style="font-family: "courier new" , "courier" , monospace;">SQLiteException</span> in these classes is going to cause a linking problem that will result in a runtime exception when the SQLite library is not included.<br />
<br />
To address this problems, I've created a pure JDBC back end that does not reference any classes from the SQLite dependency. People should be able to have much better luck with this one with an alternative JDBC driver. It also opens up a much clearer development path for someone who, if something goes wrong with the generic driver, wants to create a back end specific to their database. In fact, I've written up a quick guide on <a href="https://github.com/longevityframework/longevity/wiki/How-to-create-a-new-JDBC-back-end">how to create your own JDBC-based back end</a>. Some of this advice will be helpful for writers of non-JDBC back ends as well.<br />
<br />
The generic JDBC back end has limited support, as I am entirely unable to write tests for it without specifying a real driver. I am also currently supporting 4 other back ends if I include the in-memory back end, and it's simply more than I can handle to take on another at this point. I would be so grateful if someone could step up and create and maintain another back end, or simply to take on the maintenance of an existing back end, so I could free up some time to get on to some of other really important features. Please see the <a href="http://longevityframework.org/contributing.html">How You Can Contribute</a> page to get some ideas on how you can help. Some of the ideas here are super easy. I could really use the help, I am swamped! Thanks ahead of time.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-30123213698964909842017-01-27T17:11:00.000-05:002017-01-31T11:30:45.103-05:00Ten Ways You Can Contribute To An Open Source Project<span style="font-family: inherit;">Okay, so it's really about <b>my</b> open source project. But that's the whole point. I don't want it to be <i>my</i> project. I want it to be your project too! While this article is written to specifically address contributing to <a href="https://github.com/longevityframework/longevity">longevity</a>, much of the material here will probably apply to other projects as well.</span><br />
<span style="font-family: inherit;"><span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">This is a complete rewrite of the <a href="http://longevityframework.org/contributing.html">contributing page on the longevity web site</a>. You can read it over there if you like. I've also pasted the contents below for your convenience:</span></span><br />
<a name='more'></a><span style="font-family: inherit;"><br /></span>
<br />
<h1 style="font-size: 2.8em; font-weight: normal; letter-spacing: -1px; margin: 0px; text-indent: 6px;">
<span style="background-color: white;"><span style="font-family: inherit;">How you can contribute</span></span></h1>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">I’ve been working on longevity for a little over two years, more or less entirely on my own. It’s been a lot of fun, and longevity has developed into a truly excellent piece of technology. But sooner or later, longevity is going to need more community involvement if it is going to keep going.</span></span></div>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">Fortunately, there are many things that someone like yourself can do, and many of them require very little effort on your part. Here’s a list of suggestions of things you could do to help, ordered roughly from the least to most amount of effort on your part.</span></span></div>
<h2 id="try-it-out" style="font-size: 22px; margin-bottom: 8px; text-indent: 4px;">
<span style="background-color: white;"><span style="font-family: inherit;">Try it out</span></span></h2>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">This one is easy. Use it wherever you can. Now that we have a SQLite back end as well, there is really no excuse to not try it out in a simple application that requires some persistence. If you don’t have any projects that you can try it out on right away, play around with one of the many demo projects I’ve developed (<a href="https://github.com/longevityframework/demo" style="text-decoration: none;">demo</a>, <a href="https://github.com/longevityframework/simbl" style="text-decoration: none;">simbl</a>, <a href="https://github.com/longevityframework/simbl-play" style="text-decoration: none;">simbl-play</a>).</span></span></div>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">Longevity wants and needs users. Don’t be afraid to give it a try!</span></span></div>
<h2 id="share-your-impressions" style="font-size: 22px; margin-bottom: 8px; text-indent: 4px;">
<span style="background-color: white;"><span style="font-family: inherit;">Share your impressions</span></span></h2>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">If you’ve used longevity, or even just read some of the documentation, you are in a wonderful position to give us some feedback. Please, tell us what you think! What’s good about it? What do you dislike about it? What did you find awkward or confusing? What’s missing? You would not believe how interested we are going to be to hear your feedback. Jump on the <a href="https://gitter.im/longevityframework/longevity" style="text-decoration: none;">Gitter channel</a>, or go to the <a href="https://groups.google.com/forum/#!forum/longevity-users" style="text-decoration: none;">user group</a>, and share your thoughts, ask questions, just say hi, whatever. Not only does this give us good feedback, but it helps other users and potential users feel and see that there is a community here.</span></span></div>
<h2 id="tell-people-about-us" style="font-size: 22px; margin-bottom: 8px; text-indent: 4px;">
<span style="background-color: white;"><span style="font-family: inherit;">Tell people about us</span></span></h2>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">As of now, longevity has relied almost exclusively on grass roots methods for getting the word out - blog posts, tweets, talks at meetups, and begging for mentions in Scala newsletters. So far, awareness in the framework as grown slowly but steadily. We are always looking for new ways to spread awareness, but we would be very thankful if you want to help out in our grass roots efforts. You can tweet about us, retweet our tweets and blogposts, or share in conversations. We love you for it!</span></span></div>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">If you really want to use longevity at your job, talk with the people who make the technology choices at your company. Talk to your project managers, architects, and engineers. Point them to the site, ask them to take a look. You don’t have to pressure them at all - just let them know you would be interested in using it! It’s a data point that they can use, and you may be exposing them to a technology they haven’t heard of before.</span></span></div>
<h2 id="report-bugs-or-other-problems" style="font-size: 22px; margin-bottom: 8px; text-indent: 4px;">
<span style="background-color: white;"><span style="font-family: inherit;">Report bugs or other problems</span></span></h2>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">Please please please, if you try longevity and something goes wrong, please let us know! We have a very rigorous test suite, and we test longevity in multiple environments for every release. But there is absolutely no way that we can test ahead of time for the ways <em>you</em> are going to use longevity. If you find something wrong, and don’t tell us, <strong>we will never know about it</strong>. This would be extremely unfortunate, because if there are any bugs or other problems, we definitely want to fix them as quickly as possible.</span></span></div>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">If there is something in the documentation that doesn’t make sense, if something doesn’t seem to work like it’s supposed to, or if something is obviously broken, the best thing you can do is to go over to the <a href="https://groups.google.com/forum/#!forum/longevity-users" style="text-decoration: none;">user group</a> and tell us about it. Of course, if you just want to <a href="https://github.com/longevityframework/longevity/issues" style="text-decoration: none;">submit an issue directly</a> on GitHub, that’s fine too. Whatever you do, let us know. You are getting longevity for free, and I’ve put in a massive amount of effort to give it to you. Even if it doesn’t work out for you, you should at least give back in this small way.</span></span></div>
<h2 id="fix-problems-in-the-website" style="font-size: 22px; margin-bottom: 8px; text-indent: 4px;">
<span style="background-color: white;"><span style="font-family: inherit;">Fix problems in the website</span></span></h2>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">It should be clear to you by now that if you find a typo, a grammatical error, or a broken link on the longevity website, we want you to tell us about it. But if you are feeling generous, you can save us a little effort by fixing the problem yourself. No matter how small the fix, you will get to practice the process of forking and submitting a pull request. If you haven’t done this much, it’s really easy and a good skill to have. If you’ve done it often, then you already know how trivial it is to do. The documentation is on the <a href="https://github.com/longevityframework/longevity/tree/gh-pages" style="text-decoration: none;">gh-pages branch</a>, and you can generate it locally with <a href="https://jekyllrb.com/" style="text-decoration: none;">Jekyll</a>.</span></span></div>
<h2 id="website-design" style="font-size: 22px; margin-bottom: 8px; text-indent: 4px;">
<span style="background-color: white;"><span style="font-family: inherit;">Website design</span></span></h2>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">If you have any skills in website design, you already know that my website design skills are limited. If you can help me with the layout, and the look and feel, I would really appreciate it. Just talk to me about it first. Let’s make sure your ideas work for me as well before you put too much effort into it!</span></span></div>
<h2 id="fix-a-bug" style="font-size: 22px; margin-bottom: 8px; text-indent: 4px;">
<span style="background-color: white;"><span style="font-family: inherit;">Fix a bug</span></span></h2>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">If you find a bug and report a bug, and we can reproduce it, then we will fix it. But if you are looking to make a strong contribution, maybe you could try to fix it yourself first! Feel free to start a discussion about the bug on Gitter or the user group. We may well have some helpful ideas about how to go about fixing it. We certainly will be willing to give you whatever support we can.</span></span></div>
<h2 id="implement-a-feature" style="font-size: 22px; margin-bottom: 8px; text-indent: 4px;">
<span style="background-color: white;"><span style="font-family: inherit;">Implement a feature</span></span></h2>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">We are quickly converging on what I consider a complete product. What I mean by that is, a couple more features down, and nobody will be able to say, “I’m not using longevity because it doesn’t do <em>this</em>!” In other words, no show-stopper missing features. Of course, there are any number of features that would still be really useful to have - just take a look at the <a href="https://www.pivotaltracker.com/n/projects/1231978" style="text-decoration: none;">story board</a>!</span></span></div>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">Adding a missing a feature would make great a contribution to the project, for which we would be eternally grateful. We would be more than happy to do whatever we can to help you get started. Please write us in the <a href="https://groups.google.com/forum/#!forum/longevity-users" style="text-decoration: none;">user group</a> to tell us what you have in mind, or to ask any questions about what might be good to work on.</span></span></div>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">I’ve tagged a good handful of stories as <a href="https://www.pivotaltracker.com/n/projects/1231978/search?q=label%3A%22low%20hanging%20fruit%22" style="text-decoration: none;">low hanging fruit</a> on our story board. Take a look and see if there are any that strike your interest. Note that “low hanging fruit” does not necessarily mean super easy. What it really means is something more like “peripheral”. There is a pretty dense core of code in longevity, and working in that dense core will take some effort to get familiar with. The low hanging fruit issues are issues that don’t really touch that core.</span></span></div>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">Of course, we would also happily consider features that are not currently found on the board. Once again, please write to us first in the user group to see if it fits in to our project vision.</span></span></div>
<h2 id="maintain-a-back-end" style="font-size: 22px; margin-bottom: 8px; text-indent: 4px;">
<span style="background-color: white;"><span style="font-family: inherit;">Maintain a back end</span></span></h2>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">I’m currently maintaining four back ends. This means that, for certain kinds of features, I have to implement them four times before I can sign off. There is absolutely no way I can put in another back end without some help from the community. If there is a missing back end that you are interested in, you could consider becoming the implementor and maintainer for that back end. Or if there is an existing back end that you are already using, please consider becoming a maintainer for that back end, to free up some of my time.</span></span></div>
<h2 id="get-your-company-to-sponsor" style="font-size: 22px; margin-bottom: 8px; text-indent: 4px;">
<span style="background-color: white;"><span style="font-family: inherit;">Get your company to sponsor</span></span></h2>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">Is your company the kind of company that would like seeing their logo on the website of an open source software project that exists for the greater good of all humanity? There are any number of things your company could do to becoming a longevity sponsor. Here’s an idea that works well as an example:</span></span></div>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">Does your boss say that longevity looks great, but they are worried about how it is going to perform under load? Let’s set up a joint project where we build a performance testing suite for longevity, and see how it does compared to raw database driver interactions. Your company could contribute the developer hours to the project, or they could just pay me to do the work. If any performance problems are exposed, we could extend the joint project to work on resolving the problems.</span></span></div>
<div style="font-size: 15px; margin-bottom: 20px;">
<span style="background-color: white;"><span style="font-family: inherit;">If there are any performance problems in longevity, believe me, I want to know about them, and I want to fix them. But my time and good will efforts are limited, so in the absence of any external incentives, identifying and fixing performance problems are simply two more items on a very crowded priority list. The point here is, if your boss gripes about some limitation of longevity when you ask them about using it, there are lots of creative ways to address those limitations that we can look into. Compare it to the cost of developing and maintaining the entire persistence layer for you database application! It shouldn’t take a whole lot of persuasion.</span></span></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-52547047743819964362017-01-16T17:58:00.000-05:002017-01-16T17:58:19.370-05:00New SQLite Back End for Longevity<a href="http://longevityframework.org/">Longevity</a> release <a href="https://github.com/longevityframework/longevity/releases/tag/0.20.0">0.20.0</a> is out, featuring the following changes:<br />
<br />
<ul>
<li>Added a <a href="https://www.sqlite.org/">SQLite</a> back end.</li>
<li>Trying to be a bit more formal, changed occurrences of "Mongo" with "MongoDB" in the public API.</li>
<li>Replaced "partition key" terminology with "primary key". I was undecided about this for a long time leading up to implementing the feature, and I think I made the wrong choice when the time came. "Partition key" is a little over specified for distributed database scenario. It's a little more general to posit that a database table can have multiple keys, but can more powerfully optimize a single one of those keys at your choosing. Now that I have three very different back ends, it's getting harder and harder to overfit longevity to a single database technology!</li>
</ul>
<br />
Keep reading to learn more about the new back end:<br />
<br />
<a name='more'></a><br />
Over the last year or so, I've gotten a small handful of requests for a SQLite longevity back end. This sort of surprised me at first, but now that I've thought it over a good deal, and had some experience using SQLite, it makes a lot more sense to me.<br />
<br />
SQLite is a very lightweight way to get started doing persistence. You don't need to install anything on the machines that are going to run it - you just need to supply the SQLite jar on your classpath. SQLite stores your database in a single file in the local filesystem. This incredible ease of use is a great combination with longevity, where you don't need to define a database schema either! Now, to get started working on a database application, all you need to do it put some longevity annotations on the classes you want to persist, like in this <a href="https://github.com/longevityframework/demo/blob/master/src/main/scala/demo.scala">demo.scala example</a>.<br />
<br />
This use case is in contrast to existing MongoDB and Cassandra back ends. In these cases, we are probably planning on building larger, more involved applications, in which case the simplicity of using longevity keeps the total complexity of the application down. Program complexity is minimized in the persistence arena, to allow for more focus on the domain model and business needs.<br />
<br />
A SQLite back end also brings up the very interesting possibility of building mobile applications with Scala and longevity. I'd like to give this a try on Android when I have the chance. I'm not sure what the Scala story is on iOS these days, but I know you can use SQLite over there.<br />
<br />
In any event, the SQLite back end is in place! I hope you all love it. This is more or less a vanilla JDBC implementation, so it probably shouldn't be too far from working in conjunction with other JDBC drivers. Please experiment with other drivers if you like. I've left open a back door configuration variable <span style="font-family: Courier New, Courier, monospace;">longevity.sqlite.jdbcDriverClass</span> that you can use. If there are any simple changes I can make to get longevity working with the JDBC driver of your choice, I will be more than happy to make them. Unfortunately, I am already maintaining four longevity back ends, and I won't be able to officially support any more than that on my own. Please considering contributing!<br />
<br />
I'm not really sure what's going to come next in longevity. I still think support for optional properties is fairly critical, and that will probably be my next feature. But I'm also starting to think pretty seriously about the schema migration story. I have a pretty clear idea of what I want to do there, and it's going to be awesome. It's also a critical feature for the longevity user experience.<br />
<br />
<br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-11294510261407080892016-12-08T14:49:00.000-05:002017-01-16T12:33:26.915-05:00More Longevity Awesomeness with Macro Annotations!I just got <a href="http://longevityframework.org/">longevity</a> release 0.18 out and wow, is it awesome! If you've looked at longevity before, you will be amazed at how easy it has become to start persisting your domain objects. And the best part is that everything persistence related is tucked away in the annotations. Your domain classes are completely free of persistence concerns, expressing your domain model perfectly, and ready for use in all portions of your application. Of course, we also provide a complete persistence layer for you, saving you countless hours of development effort!<br />
<br />
See for yourself, here is a simple example demonstrating how easy it is to get started:<br />
<br />
<a name='more'></a><pre class="highlight" style="background: rgb(255, 255, 255); border: 1px solid rgb(242, 242, 242); color: #222222; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; font-size: 13px; margin-bottom: 30px; overflow: auto; padding: 20px; text-shadow: none;"><code style="border: none; color: #2879d0; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; margin-bottom: 30px; padding: 0px;"><span class="k" style="color: #aa22ff; font-weight: bold;">package</span> <span class="nn" style="color: blue; font-weight: bold;">domainModel</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">import</span> <span class="nn" style="color: blue; font-weight: bold;">longevity.model.annotations._</span>
<span class="c1" style="color: #008800; font-style: italic;">// first, define our domain classes:
</span>
<span class="c1" style="color: #008800; font-style: italic;">// the @persistent is the thing we want to persist in its own table.
</span> <span class="c1" style="color: #008800; font-style: italic;">// the primaryKey describes how we want to retrieve the objects.
</span>
<span class="nd" style="color: #aa22ff;">@persistent</span><span class="o" style="color: #666666;">(</span><span class="n">keySet</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="nc" style="color: blue;">Set</span><span class="o" style="color: #666666;">(</span><span class="n">primaryKey</span><span class="o" style="color: #666666;">(</span><span class="nc" style="color: blue;">User</span><span class="o" style="color: #666666;">.</span><span class="n">props</span><span class="o" style="color: #666666;">.</span><span class="n">username</span><span class="o" style="color: #666666;">)))</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">User</span><span class="o" style="color: #666666;">(</span>
<span class="n">username</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Username</span><span class="o" style="color: #666666;">,</span>
<span class="n">email</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Email</span><span class="o" style="color: #666666;">,</span>
<span class="n">fullName</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">FullName</span><span class="o" style="color: #666666;">)</span>
<span class="c1" style="color: #008800; font-style: italic;">// @keyVal means we can retrieve users by username:
</span>
<span class="nd" style="color: #aa22ff;">@keyVal</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">User</span><span class="o" style="color: #666666;">]</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">Username</span><span class="o" style="color: #666666;">(</span><span class="n">username</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">)</span>
<span class="c1" style="color: #008800; font-style: italic;">// a @component is a part of the object we want to persist:
</span>
<span class="nd" style="color: #aa22ff;">@component</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">Email</span><span class="o" style="color: #666666;">(</span><span class="n">email</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">)</span>
<span class="nd" style="color: #aa22ff;">@component</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">FullName</span><span class="o" style="color: #666666;">(</span>
<span class="n">last</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">,</span>
<span class="n">first</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">,</span>
<span class="n">title</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Option</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">])</span>
<span class="c1" style="color: #008800; font-style: italic;">// gather all the domain classes into a domain model:
</span>
<span class="nd" style="color: #aa22ff;">@domainModel</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">object</span> <span class="nc" style="color: blue;">DomainModel</span>
<span class="o" style="color: #666666;">}</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">object</span> <span class="nc" style="color: blue;">applicationServices</span> <span class="k" style="color: #aa22ff; font-weight: bold;">extends</span> <span class="nc" style="color: blue;">App</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">import</span> <span class="nn" style="color: blue; font-weight: bold;">domainModel._</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">import</span> <span class="nn" style="color: blue; font-weight: bold;">longevity.context.LongevityContext</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">import</span> <span class="nn" style="color: blue; font-weight: bold;">scala.concurrent.ExecutionContext.Implicits.global</span>
<span class="c1" style="color: #008800; font-style: italic;">// build the context for our domain model
</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">context</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="nc" style="color: blue;">LongevityContext</span><span class="o" style="color: #666666;">(</span><span class="nc" style="color: blue;">DomainModel</span><span class="o" style="color: #666666;">)</span>
<span class="c1" style="color: #008800; font-style: italic;">// get the user repository
</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">userRepo</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="n">context</span><span class="o" style="color: #666666;">.</span><span class="n">repoPool</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">User</span><span class="o" style="color: #666666;">]</span>
<span class="c1" style="color: #008800; font-style: italic;">// we are now ready to start persisting users
</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">username</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="nc" style="color: blue;">Username</span><span class="o" style="color: #666666;">(</span><span class="s" style="color: #bb4444;">"sméagol"</span><span class="o" style="color: #666666;">)</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">oldEmail</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="nc" style="color: blue;">Email</span><span class="o" style="color: #666666;">(</span><span class="s" style="color: #bb4444;">"gollum@gmail.example.com"</span><span class="o" style="color: #666666;">)</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">newEmail</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="nc" style="color: blue;">Email</span><span class="o" style="color: #666666;">(</span><span class="s" style="color: #bb4444;">"sméagol@gmail.example.com"</span><span class="o" style="color: #666666;">)</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">fullName</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="n">context</span><span class="o" style="color: #666666;">.</span><span class="n">testDataGenerator</span><span class="o" style="color: #666666;">.</span><span class="n">generate</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">FullName</span><span class="o" style="color: #666666;">]</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">user</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="nc" style="color: blue;">User</span><span class="o" style="color: #666666;">(</span><span class="n">username</span><span class="o" style="color: #666666;">,</span> <span class="n">oldEmail</span><span class="o" style="color: #666666;">,</span> <span class="n">fullName</span><span class="o" style="color: #666666;">)</span>
<span class="c1" style="color: #008800; font-style: italic;">// create, retrieve, update, delete
</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">f</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="k" style="color: #aa22ff; font-weight: bold;">for</span> <span class="o" style="color: #666666;">{</span>
<span class="n">created</span> <span class="k" style="color: #aa22ff; font-weight: bold;"><-</span> <span class="n">userRepo</span><span class="o" style="color: #666666;">.</span><span class="n">create</span><span class="o" style="color: #666666;">(</span><span class="n">user</span><span class="o" style="color: #666666;">)</span>
<span class="n">retrieved</span> <span class="k" style="color: #aa22ff; font-weight: bold;"><-</span> <span class="n">userRepo</span><span class="o" style="color: #666666;">.</span><span class="n">retrieveOne</span><span class="o" style="color: #666666;">(</span><span class="n">username</span><span class="o" style="color: #666666;">)</span>
<span class="n">modified</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="n">retrieved</span><span class="o" style="color: #666666;">.</span><span class="n">map</span><span class="o" style="color: #666666;">(</span><span class="k" style="color: #aa22ff; font-weight: bold;">_</span><span class="o" style="color: #666666;">.</span><span class="n">copy</span><span class="o" style="color: #666666;">(</span><span class="n">email</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="n">newEmail</span><span class="o" style="color: #666666;">))</span>
<span class="n">updated</span> <span class="k" style="color: #aa22ff; font-weight: bold;"><-</span> <span class="n">userRepo</span><span class="o" style="color: #666666;">.</span><span class="n">update</span><span class="o" style="color: #666666;">(</span><span class="n">modified</span><span class="o" style="color: #666666;">)</span>
<span class="n">deleted</span> <span class="k" style="color: #aa22ff; font-weight: bold;"><-</span> <span class="n">userRepo</span><span class="o" style="color: #666666;">.</span><span class="n">delete</span><span class="o" style="color: #666666;">(</span><span class="n">updated</span><span class="o" style="color: #666666;">)</span>
<span class="o" style="color: #666666;">}</span> <span class="k" style="color: #aa22ff; font-weight: bold;">yield</span> <span class="o" style="color: #666666;">{</span>
<span class="n">println</span><span class="o" style="color: #666666;">(</span><span class="n">s</span><span class="s" style="color: #bb4444;">"created ${created.get}"</span><span class="o" style="color: #666666;">)</span>
<span class="n">println</span><span class="o" style="color: #666666;">(</span><span class="n">s</span><span class="s" style="color: #bb4444;">"retrieved ${retrieved.get}"</span><span class="o" style="color: #666666;">)</span>
<span class="n">println</span><span class="o" style="color: #666666;">(</span><span class="n">s</span><span class="s" style="color: #bb4444;">"updated ${updated.get}"</span><span class="o" style="color: #666666;">)</span>
<span class="n">println</span><span class="o" style="color: #666666;">(</span><span class="n">s</span><span class="s" style="color: #bb4444;">"deleted ${deleted.get}"</span><span class="o" style="color: #666666;">)</span>
<span class="o" style="color: #666666;">}</span>
<span class="c1" style="color: #008800; font-style: italic;">// wait for the CRUD ops to complete
</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">import</span> <span class="nn" style="color: blue; font-weight: bold;">scala.concurrent.Await</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">import</span> <span class="nn" style="color: blue; font-weight: bold;">scala.concurrent.duration.Duration</span>
<span class="nc" style="color: blue;">Await</span><span class="o" style="color: #666666;">.</span><span class="n">result</span><span class="o" style="color: #666666;">(</span><span class="n">f</span><span class="o" style="color: #666666;">,</span> <span class="nc" style="color: blue;">Duration</span><span class="o" style="color: #666666;">.</span><span class="nc" style="color: blue;">Inf</span><span class="o" style="color: #666666;">)</span>
<span class="c1" style="color: #008800; font-style: italic;">// close db connection after CRUD ops complete
</span>
<span class="n">context</span><span class="o" style="color: #666666;">.</span><span class="n">repoPool</span><span class="o" style="color: #666666;">.</span><span class="n">closeSession</span><span class="o" style="color: #666666;">()</span>
<span class="o" style="color: #666666;">}</span>
</code></pre>
<div>
You can find this example <a href="https://github.com/longevityframework/demo">on GitHub</a>. It should pretty much run for you right out of the box. Try setting <span style="font-family: "courier new" , "courier" , monospace;">longevity.backEnd</span> to <span style="font-family: "courier new" , "courier" , monospace;">Mongo</span> or <span style="font-family: "courier new" , "courier" , monospace;">Cassandra</span> in <span style="font-family: "courier new" , "courier" , monospace;">application.conf</span> if you have one of them installed locally.</div>
<br />
Writing <a href="http://docs.scala-lang.org/overviews/macros/annotations">macro annotations</a> is so much fun! I started out writing a single macro, and ended up writing nine. My initial goal was removing the boilerplate in describing the <i>properties</i> of a persistent class, i.e., the ability to talk meta about members of classes like the <span style="font-family: "courier new" , "courier" , monospace;">User.username</span> field above. With the <span style="font-family: "courier new" , "courier" , monospace;">@persistent</span> annotation, this property is simply <span style="font-family: "courier new" , "courier" , monospace;">User.props.username</span>. But it occurred to me that I could write a small handful of annotations that would allow for describing a domain model using nothing but the annotations.<br />
<br />
Of course, all longevity functionality is available without using the macro annotations! But I don't see any advantage in doing so, aside from not having to <a href="http://stackoverflow.com/questions/41002966/easiest-way-for-users-to-include-scalamacros-paradise">enable macro paradise in your build</a>.<br />
<br />
Well, what a relief to get this out! I feel like a true 1.0 feature set is now in place. But because the last few releases have brought some major API changes, I plan on holding off on committing to a 1.0 release for a while, to give the changes some time to settle.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-10674524813974853752016-11-15T13:26:00.000-05:002016-12-19T17:51:24.464-05:00More API Simplifications in Longevity 0.17<a href="http://longevityframework.org/">Longevity</a> is the persistence framework to use with Scala and NoSQL. Release 0.17 simplifies the user API in some very pleasant ways. Empty marker traits <span style="font-family: "courier new" , "courier" , monospace;">Persistent</span> and <span style="font-family: "courier new" , "courier" , monospace;">Embeddable</span> are removed. Also, <span style="font-family: "courier new" , "courier" , monospace;">KeyVal</span> now takes a single type parameter instead of two. In brief, this will make defining the classes you want to persist so much easier and more natural, as you no longer have to extend your persistent classes with empty marker traits.<br />
<br />
<a name='more'></a><br />
I've been unhappy with the <span style="font-family: "courier new" , "courier" , monospace;">Persistent</span> and <span style="font-family: "courier new" , "courier" , monospace;">Embeddable</span> empty marker traits for a while now, but I haven't been certain enough until now to commit to removing them. My internal thought process was roughly something like this: Right now, the longevity repository class has a signature like so:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">trait Repo[P <: Persistent]</span><br />
<br />
If I change that to look like this:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">trait Repo[P]</span><br />
<br />
Then it won't be clear that only persistent objects have repositories. But that argument doesn't really hold up, because you can only ever get repositories for persistent objects, as longevity creates the repositories for you. Also, it doesn't even make sense to have a repository for a type that you don't want to persist.<br />
<br />
In the end, the fact that these traits were empty, and providing no functionality, was a smell. I was easily able to remove them, and I adjusted my <a href="http://longevityframework.github.io/longevity/manual/">User Manual</a> and <a href="http://longevityframework.github.io/longevity/getting-started/">Getting Started Guide</a> to talk consistently about <i>persistent objects</i> and <i>persistent components</i>. (I adjusted my vocabulary to replace the term <i>embeddable</i> with <i>persistent component</i>, which is probably more intuitive in users' minds.) The fact that the user manual speaks concretely about these concepts obviates the needs for them to be included in the type system.<br />
<br />
So I'm really happy with the way the new API looks. Looking at the sample applications (<a href="https://github.com/longevityframework/simbl">Akka HTTP</a> and <a href="https://github.com/longevityframework/simbl-play">Play</a> versions) and user manual examples, it feels so much nicer! I hope you enjoy the changes as much as I do.<br />
<br />
I decided to release these minor changes as a separate release because it might be nice for users to have the chance to adjust to the new API without having to worry about other changes at the same time. Also, my next job will be a deep dive into Scala macros, and it may be a while before I put out the next release!Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-14257898564260420352016-11-11T13:46:00.001-05:002016-12-19T17:51:45.898-05:00Longevity Artifacts for Scala 2.11 and 2.12So I've finally got the <a href="http://longevityframework.org/">longevity</a> build set up to publish artifacts for Scala versions 2.11 and 2.12! I'm a little behind on the game, but, I've been busy ;-). Please use longevity version 0.16.1 for 2.12 artifacts. I'll be publishing artifacts for both Scala versions moving forward.<br />
<br />
I'd like to shout out to the wonderful people at <a href="http://www.scalatest.org/">ScalaTest</a> - <a href="https://twitter.com/bvenners">Bill Venners</a> and <a href="https://twitter.com/chuacheeseng83">Chua Chee Seng</a> - for helping me resolve an interoperability issue that made the longevity 2.12 artifacts possible. Thanks again!Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-52473920060871930832016-11-10T18:08:00.000-05:002016-12-19T17:51:57.156-05:00Partition Keys in LongevityI'm so excited to announce the release of longevity version 0.16.0! <a href="http://longevityframework.org/">Longevity</a> now supports partition keys - a key where, given a key value, we can determine the node in a distributed database where the data resides (or would reside, if it existed). This is a critical feature for NoSQL databases in a distributed context. A partition key is implemented with <a href="https://docs.mongodb.com/manual/sharding/#shard-keys">sharding</a> in MongoDB, and with <a href="https://docs.datastax.com/en/cql/3.1/cql/cql_reference/refCompositePk.html">partitioning</a> in Cassandra.<br />
<br />
I'll provide a brief example of a partition key here after the jump, but to learn more about it, please take a look at the <a href="http://longevityframework.github.io/longevity/manual/ptype/partition-keys.html">user manual</a>.<br />
<br />
<a name='more'></a><br />
Suppose you are persisting users, and you want your user data to be partitioned by username. Here's how it would look:<br />
<br />
<pre class="highlight" style="background: rgb(255, 255, 255); border: 1px solid rgb(242, 242, 242); color: #222222; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; margin-bottom: 30px; overflow: auto; padding: 20px; text-shadow: none;"><code style="border: none; color: #2879d0; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; margin-bottom: 30px; padding: 0px;"><span style="font-size: x-small;"><span class="k" style="color: #aa22ff; font-weight: bold;">import</span> <span class="nn" style="color: blue; font-weight: bold;">longevity.subdomain.KeyVal</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">import</span> <span class="nn" style="color: blue; font-weight: bold;">longevity.subdomain.Persistent</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">import</span> <span class="nn" style="color: blue; font-weight: bold;">longevity.subdomain.PType</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">Username</span><span class="o" style="color: #666666;">(</span><span class="n">username</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">)</span> <span class="k" style="color: #aa22ff; font-weight: bold;">extends</span> <span class="nc" style="color: blue;">KeyVal</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">User</span>, <span class="kt" style="color: #00bb00; font-weight: bold;">Username</span><span class="o" style="color: #666666;">]</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">User</span><span class="o" style="color: #666666;">(</span>
<span class="n">username</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Username</span><span class="o" style="color: #666666;">,</span>
<span class="n">firstName</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">,</span>
<span class="n">lastName</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">)</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">extends</span> <span class="nc" style="color: blue;">Persistent</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">object</span> <span class="nc" style="color: blue;">User</span> <span class="k" style="color: #aa22ff; font-weight: bold;">extends</span> <span class="nc" style="color: blue;">PType</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">User</span><span class="o" style="color: #666666;">]</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">object</span> <span class="nc" style="color: blue;">props</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">username</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="n">prop</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">Username</span><span class="o" style="color: #666666;">](</span><span class="s" style="color: #bb4444;">"username"</span><span class="o" style="color: #666666;">)</span>
<span class="o" style="color: #666666;">}</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">object</span> <span class="nc" style="color: blue;">keys</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">username</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="n">partitionKey</span><span class="o" style="color: #666666;">(</span><span class="n">props</span><span class="o" style="color: #666666;">.</span><span class="n">username</span><span class="o" style="color: #666666;">)</span>
<span class="o" style="color: #666666;">}</span>
<span class="o" style="color: #666666;">}</span></span></code></pre>
Pretty simple huh?<br />
<br />
Partition keys was the last longevity feature that I've felt was essential to get into a 1.0 release. Because I am fully committed to <a href="http://semver.org/">semantic versioning</a>, I have a new set of features at the top of list now: Features that are both important, and are known or suspected to break backwards compatibility. These are some really neat and exciting features that will make longevity more powerful and easier for you to use:<br />
<ul>
<li><b>Persist Custom Types</b>: Currently, all the types you want to persist need to be case classes with a single parameter list. I plan to enhance things so that you can persist any types you want. Roughly speaking, all you would need to do would be to supply <span style="font-family: "courier new" , "courier" , monospace;">apply</span> and <span style="font-family: "courier new" , "courier" , monospace;">unapply</span> methods similar to those that are generated for you for case classes. This would allow you to persist classes that were not case classes, including types provided by third-party libraries.</li>
<ul>
<li>One off-shoot to this feature is that I will end up removing the need to have your classes extend empty traits, like the <span style="font-family: "courier new" , "courier" , monospace;">case class User ... extends Persistent</span> above. I think most users will like that. I'm thinking I will remove the <span style="font-family: "courier new" , "courier" , monospace;">Persistent</span> and related traits entirely. What do you think?</li>
</ul>
<li><b>Macro for Persistent Properties</b>: Currently, all the properties you use in your keys, indexes and queries need to be manually defined by the user. For each one, you have to specify both the type and the property path. I'm planning to write a macro that will generate the complete set of properties for you. This will take some effort, and I'm not 100% sure at this point that it's going to fly, but I think it will work.</li>
<li><b>Classpath Scanning for Subdomain</b>: Currently, the user has to re-enumerate all the types they want to persist when building the subdomain. I plan on writing a classpath scanning routine that will collect them all for you, saving you some busy work, and avoiding bugs involved with failing to keep the list up to date.</li>
</ul>
<div>
So what do you think, which of these features would you like to see first? I plan to tackle them in the order presented here. What other features or changes would you like to see? Please chime in. You can comment here, on the <a href="https://groups.google.com/forum/#!forum/longevity-users">user forum</a>, or <a href="https://twitter.com/JohnnyLongevity">send me a tweet</a>. <b>I need more feedback from you in order to steer this project in the best direction!!</b></div>
<div>
<b><br /></b></div>
<div>
Thanks so much for reading and using longevity!</div>
<br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-86372738294652401492016-10-13T13:16:00.003-04:002016-12-19T17:52:12.149-05:00Longevity 0.15.0 - Query Enhancements<a href="http://longevityframework.org/">Longevity</a> - a persistence framework for Scala and NoSQL - is now up to version 0.15.0! The latest release focuses on query enhancements. We have added orderBy, offset, and limit clauses to our queries and query DSL.<br />
<br />
<a name='more'></a>I'm very excited to have a query language that now supports orderBy, offset, and limit clauses. My feeling is this brings a level of completeness to longevity queries that is suitable for a 1.0 release. Check out the <a href="http://longevityframework.github.io/longevity/manual/query/">chapter on querying</a> in the user manual to see if you agree!<br />
<br />
The major thing missing from queries at this point is the ability to construct queries involving collection-based properties: Options, Sets and Lists. Options seem like the most important collection type to me, since it is probably quite common for users to want to query on optional fields. I anticipate starting work on this towards the end of the year. Another missing query feature is an IN operator, (e.g., <span style="font-family: "courier new" , "courier" , monospace;">username in Set("username1", "username2")</span>), which should be pretty easy to write.<br />
<br />
Unfortunately, due to the limitations of the Cassandra query language, I was only able to implement the limit clause for the Cassandra back end. Cassandra does not support offset clauses at all, and supports a very limit syntax for ordered queries. Cassandra only handles ORDER BY clauses for a single column, and it has to be the <a href="https://docs.datastax.com/en/cql/3.1/cql/cql_reference/select_r.html#reference_ds_d35_v2q_xj__using-compound-primary-keys-and-sorting-results">second column of a compound primary key</a>. The next major feature I will be working on in longevity is <a href="http://longevityframework.github.io/longevity/manual/translation/keys.html">partition keys</a>, and as part of that work, I will expand the Cassandra support for longevity queries to allow orderBy clauses where possible.<br />
<br />
Please give longevity a try! Check out the <a href="http://longevityframework.github.io/longevity/getting-started/">Getting Started Guide</a> to get an overview of how it works. We also have <a href="http://longevityframework.github.io/longevity/activator.html">Activator Tutorials</a> for using longevity with <a href="https://www.playframework.com/">Play</a> and <a href="http://doc.akka.io/docs/akka/2.4.8/scala/http/">Akka HTTP</a> front ends. And please let me know what you think on the <a href="https://groups.google.com/forum/#!forum/longevity-users">longevity discussion board</a>! I really, really want to hear about any questions you have, bugs you encounter, and features you would like to see added.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-34511757800160592042016-10-03T21:00:00.000-04:002016-12-19T17:52:24.866-05:00Sample Application for Longevity and Play FrameworkI've ported my sample <a href="http://longevityframework.org/">longevity</a> application - <a href="https://github.com/longevityframework/simbl">Simple Blogging</a> - from <a href="http://doc.akka.io/docs/akka/2.4.11/scala/http/index.html">Akka HTTP</a> to <a href="https://www.playframework.com/">Play</a>. The Play version is a <a href="https://www.lightbend.com/community/core-tools/activator-and-sbt">Lightbend Activator</a> tutorial, which you can find <a href="http://www.lightbend.com/activator/template/activator-longevity-play-tutorial">here</a>. Or you can take a look at the source code <a href="https://github.com/longevityframework/simbl-play">here on GitHub</a>.<br />
<br />
Longevity is a persistence framework for Scala and NoSQL.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-89598029072258915262016-09-26T10:37:00.000-04:002016-12-19T17:52:38.559-05:00Longevity 0.13.0 - Odds and EndsIn the past few weeks <a href="http://longevityframework.org/">longevity</a> has had three minor releases (<a href="https://github.com/longevityframework/longevity/releases/tag/0.11.0">0.11</a>, <a href="https://github.com/longevityframework/longevity/releases/tag/0.12.0">0.12</a>, and <a href="https://github.com/longevityframework/longevity/releases/tag/0.13.0">0.13</a>) that clean up a variety of odds and ends that had been building up in the backlog. There was nothing major or exciting in these releases, but they are nonetheless important improvements. I haven't really publicized them much, but I wanted to let you know that longevity development is still proceeding.<br />
<br />
Here are some changes that are worth mentioning:<br />
<ul>
<li>You no longer have to specify <span style="font-family: "courier new" , "courier" , monospace;">polyType</span> and <span style="font-family: "courier new" , "courier" , monospace;">polyPType</span> in your <span style="font-family: "courier new" , "courier" , monospace;">DerivedTypes</span> and <span style="font-family: "courier new" , "courier" , monospace;">DerivedPTypes</span>, respectively.</li>
<li>Instead of just creating schema every time, I've added <span style="font-family: "courier new" , "courier" , monospace;">RepoPool.createSchema()</span>, which you can call as you like. Schema creation is still non-destructive. And you can turn on the old behavior with configuration flag <span style="font-family: "courier new" , "courier" , monospace;">longevity.autocreateSchema</span>.</li>
<li>I added <span style="font-family: "courier new" , "courier" , monospace;">RepoPool.closeSession()</span>, so you can clean up resources held by the underlying database drivers.</li>
<li>I added some logging output.</li>
<li>I added JSON marshallers and unmarshallers to the longevity context.</li>
</ul>
<div>
Next on the list is query enhancements! Although I have a couple of things to work on other than longevity proper that may precede it. I need to debug and fix a problem that has come up with longevity tests in <a href="http://www.scalatest.org/">ScalaTest</a> version 3.0.0. And I'm thinking I'll port my sample application <a href="https://github.com/longevityframework/simbl">simbl</a> from <a href="http://doc.akka.io/docs/akka/2.4.10/scala/http/">Akka HTTP</a> to <a href="https://www.playframework.com/">Play</a>.</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-63457189665460109082016-08-26T12:53:00.000-04:002016-12-19T17:52:52.465-05:00Configuration-level Optimistic Locking with Longevity<a href="http://longevityframework.org/">Longevity</a> - a persistence framework for Scala and NoSQL - has been built from day one to isolate persistence concerns behind a clear, consistent, and easy to use API. The power of this design principle is demonstrated by how easy it is for you to bring <a href="https://en.wikipedia.org/wiki/Optimistic_concurrency_control">optimistic locking</a> to your application. All you have to do is set a configuration flag.<br />
<br />
Longevity is growing to full maturity, and most features you would expect from a persistence framework are in place. Our <a href="https://github.com/longevityframework/longevity/releases/tag/0.10.0">0.10.0 release</a> brings many incremental improvements - including <a href="http://longevityframework.github.io/longevity/manual/poly/cv.html">support for controlled vocabularies</a>. But the central feature of this release is configuration-level optimistic locking.<br />
<br />
<a name='more'></a>Let's say you're starting a new Scala project, and you want to use a NoSQL database such as <a href="https://www.mongodb.com/">MongoDB</a> or <a href="http://cassandra.apache.org/">Cassandra</a>. You start looking around for tools to help integrate the database and the application. You hear great things about <a href="http://slick.lightbend.com/">Slick</a>, but unfortunately, it's only for relational databases. For Mongo, there are good libraries such as <a href="https://mongodb.github.io/casbah/">Casbah</a> and <a href="http://reactivemongo.org/">ReactiveMongo</a>. For Cassandra, there is <a href="http://outworkers.github.io/phantom/">Phantom</a>. All these options will give you as much as the Java drivers, as well as their own styles of support for translating between database records and Scala objects. But you still have to write and maintain the translations yourselves, as well as other repository methods.<br />
<br />
You picked a good library and you are plowing ahead with an early version of your application. You build out some repositories, and find that you are going to need integration tests that hit a real database, to assure your repositories continue to work properly. Your tests need test data, so you develop a little library for producing test data for your domain. The next feature needs a new query, so you write a repository method and a test for that. You are rapidly producing persistence infrastructure that you are going to have to maintain indefinitely.<br />
<br />
Earlier on, you planned on coming back and implementing optimistic locking when the system matured a little bit. Now, you're wondering if it would be worth the effort. Let's say you were to go ahead with it. Now, every one of your domain classes has a new <span style="font-family: "courier new" , "courier" , monospace;">rowVersion</span> field in it. The JSON converter picks it up, and it starts making its way into the API. One of your API clients notices the new field and asks about it. There's nothing to say but it shouldn't be there, please ignore it, and we'll get rid of it soon.<br />
<br />
Choosing <a href="http://longevityframework.github.io/longevity/">longevity</a> for a persistence framework would have saved you a lot of trouble. It entirely handles the translation between database records and Scala objects. It produces fully functional repositories for you, out of the box. It provides you with integration tests to assure your repositories are working. It produces test data for your domain. And you get optimistic locking with the flick of the switch. No need to add any row version fields to your domain objects.<br />
<br />
Longevity 1.0 is on track to be released this fall. It's all in the Current and Backlog columns of <a href="https://www.pivotaltracker.com/n/projects/1231978">this story board</a>. And the Icebox column there will give you a good sense of the places longevity is headed. To get a better idea of what longevity already does for you, check out the <a href="http://longevityframework.github.io/longevity/feature-list.html">feature list</a>, the <a href="http://longevityframework.github.io/longevity/getting-started/">Getting Started Guide</a>, or browse the <a href="http://longevityframework.github.io/longevity/manual/">user manual</a> table of contents.<br />
<br />
If there is a feature you are interested in and you don't see it or it's not implemented yet, by all means, please <a href="https://groups.google.com/forum/#!forum/longevity-users">let me know</a>! There's a good chance we can get you that feature before you need it.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-20328988128058655832016-08-17T13:02:00.000-04:002016-12-19T17:53:12.094-05:00Longevity Framework Cares About Code QualityWe care a lot about code quality here at <a href="http://longevityframework.org/">longevity framework</a>. You can rest assured that longevity is well-designed, has clean code, and a robust test suite. As of now, code coverage comes in at 95.02% statement, and 93.44% branch, as measured by <a href="https://github.com/scoverage/sbt-scoverage">sbt-scoverage</a>. What software projects do you know of that have that kind of coverage?<br />
<br />
<a name='more'></a>Longevity relies heavily on <a href="https://github.com/longevityframework/emblem/wiki">emblem</a>, a utility library developed in tandem with longevity. Emblem code coverage is 92.03% statement, 87.96% branch. Both emblem and longevity have more test lines of code than main lines of code.<br />
<br />
Longevity has an integration test suite, comprised of tests that run against a test database, and a unit test suite. We strive for full coverage of the longevity code base against each test suite individually. The integration test suite coverage is 91.88% statement, 80.33% branch.<br />
<br />
It's not possible to write unit tests against most of packages <span style="font-family: "courier new" , "courier" , monospace;">longevity.persistence</span> and <span style="font-family: "courier new" , "courier" , monospace;">longevity.exceptions.persistence</span>, since this code involves database interactions. But in longevity code outside of these two packages, unit test coverage is very high. Package <span style="font-family: "courier new" , "courier" , monospace;">longevity.subdomain</span> has 100% unit test coverage, both statement and branch. Package longevity.context has 91.67% unit test coverage.<br />
<br />
Of course, the longevity test suite is not just about coverage percentages. There is a <a href="https://github.com/longevityframework/longevity/tree/master/src/test/scala/longevity/integration/subdomain">suite of integration tests</a> covering nearly every possible subdomain feature we support, as well as an <a href="https://github.com/longevityframework/longevity/tree/master/src/test/scala/longevity/integration/queries">integration test suite</a> for every kind of query you can write.<br />
<br />
Test coverage is just one part of code quality. We are constantly refactoring and cleaning up code as we add new features and enhance the framework. Our coding style is consistent throughout the codebase. Source code files, classes, and methods are short. Method and variable names are carefully chosen.<br />
<br />
If you've looked at our <a href="http://longevityframework.github.io/longevity/manual">User Manual</a>, <a href="http://longevityframework.github.io/longevity/getting-started/">Getting Started Guide</a>, or <a href="http://longevityframework.github.io/longevity/scaladocs/">Scaladocs</a>, you already know that we care a lot about quality user-facing documentation as well. Every public element of the longevity API has non-empty and informative Scaladoc comments. Also, nearly every code example in the user manual is included in the longevity test suite, so you can be assured of their correctness.<br />
<br />
I have a personal pet peave about the coding examples that occur in most library documentation: the import statements are not included, so that you are left guessing where the classes and types used in the example come from. This is even worse in Scala than it was in Java, since it is not always possible to do a name search in the left sidebar of the Scaladocs to find what you are looking for. Consequently, every example in the User Manual and Getting Started Guide includes a complete set of import statements, with no wildcards.<br />
<br />
Longevity is built to last, and it is built with you the user in mind.<br />
<ul>
</ul>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-31157620092322655632016-07-20T15:39:00.001-04:002016-12-19T17:53:34.949-05:00Longevity Getting Started Guide Now AvailableI'm happy to announce that the <a href="http://longevityframework.org//getting-started/">Getting Started Guide</a> for <a href="http://longevityframework.org/">longevity</a> is now available. It walks through a sample application called <a href="https://github.com/longevityframework/simbl">Simple Blogging</a>, which uses longevity on the back end, and <a href="http://doc.akka.io/docs/akka/2.4.8/scala/http/">Akka HTTP</a> on the front.<br />
<br />
This guide will walk you through the basics of using longevity within a real application.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-75273163828428514772016-07-19T12:28:00.001-04:002016-12-19T17:54:13.882-05:00Longevity now has an Activator Tutorial!Learn how to get started with the Lightbend Activator tutorial here:<br />
<br />
<a href="http://longevityframework.org/activator.html">http://longevityframework.org/activator.html</a>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-20603819089442302662016-07-15T17:23:00.001-04:002016-12-19T18:00:04.930-05:00Longevity Now Has a Streamlined API!I'm very excited to announce the 0.9.0 release of <a href="http://longevityframework.org/">longevity</a>, which features a whole slew of API improvements that make longevity much easier to use and understand. Here are the three most significant changes:<br />
<br />
<ul>
<li><span style="font-family: "courier new" , "courier" , monospace;">Shorthands</span> are gone</li>
<li><span style="font-family: "courier new" , "courier" , monospace;">Assocs</span> are gone</li>
<li>The API for <span style="font-family: "courier new" , "courier" , monospace;">Key</span> and <span style="font-family: "courier new" , "courier" , monospace;">KeyVal</span> has been reworked for significant ease-of-use improvements</li>
</ul>
<div>
I'll discuss each of these changes in more detail below.</div>
<div>
<a name='more'></a><br />
<h3>
Shorthands</h3>
</div>
<div>
First of all, shorthands. These were essentially <a href="http://longevityframework.github.io/longevity/manual/embeddable/value-objects.html">value objects</a> containing a single field. For instance, we might have a <span style="font-family: "courier new" , "courier" , monospace;">Username</span> type that we use in our domain model instead of a raw string, for improved type-safety:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">case class Username(username: String)</span></div>
<div>
<br /></div>
<div>
The only advantage to using shorthands over value objects was for a more compact JSON representation. Previously, if you chose to make <span style="font-family: "courier new" , "courier" , monospace;">Username</span> a value object, you would get JSON like this for a user:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">{</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> username: {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> username: "smithy"</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> },</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> email: "smithy@example.com",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> fullName: "John Smith"</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">}</span></div>
<div>
<br /></div>
<div>
If you chose to use a shorthand instead, you would get JSON like this:</div>
<div>
<br /></div>
<br />
<div>
<span style="font-family: "courier new" , "courier" , monospace;">{</span></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> </span><span style="font-family: "courier new" , "courier" , monospace;">username: "smithy"</span><span style="font-family: "courier new" , "courier" , monospace;">,</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> email: "smithy@example.com",</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> fullName: "John Smith"</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">}</span></div>
</div>
<br />
<div>
<br /></div>
<div>
As of the 0.9 release, all single-field <a href="http://longevityframework.github.io/longevity/manual/embeddable/">embeddables</a> are inlined in the JSON in this way, so there is no longer any advantage in using shorthands.</div>
<div>
<br /></div>
<div>
The end result is that shorthands no longer serve a purpose, and are now gone. This is a wonderful simplification to the API that I hope users will appreciate.<br />
<br /></div>
<h3>
Assocs</h3>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">Assocs</span> used to be a way to create relationships between two persistent objects. In traditional Domain Driven Design terminology, this would be a <a href="http://aviadezra.blogspot.com/2009/05/uml-association-aggregation-composition.html">aggregative</a> relationship between two entity aggregates. Aside from being misnamed (<i>association</i> instead of <i>aggregation</i>), they were strange, and largely redundant with <span style="font-family: "courier new" , "courier" , monospace;">Keys</span> and <span style="font-family: "courier new" , "courier" , monospace;">KeyVals</span>. I've been uncomfortable with them for a while, but I was hesitant to pull them because the API for using <span style="font-family: "courier new" , "courier" , monospace;">Keys</span> was both cumbersome and confusing. I finally had enough while I was working on a sample application, and decided it was time to fix up the API for using <span style="font-family: "courier new" , "courier" , monospace;">Keys</span> and <span style="font-family: "courier new" , "courier" , monospace;">KeyVals</span>. This allowed me to remove <span style="font-family: "courier new" , "courier" , monospace;">Assocs</span> with no loss of convenience or functionality. Once again, this is a great API simplification - the smaller the better! And I'm optimistic that people will find the new Keys API intuitive and natural to use.<br />
<br /></div>
<h3>
KeyVals and Keys</h3>
<div>
<a href="http://longevityframework.github.io/longevity/manual/ptype/keys.html">Keys</a> are a way to declare a specific kind of meta-information about a persistent type: that no two persistent objects should have the same value for that key. They also provide a way to lookup up objects from your persistent store. Unfortunately, the original longevity design for keys focused on the former quality, at the expense of the latter. A key was a sequence of properties of the persistent object that would together make the object unique. You could build key values for database lookup, but the API was clumsy and not very type-safe.</div>
<div>
<br /></div>
<div>
The biggest hurdle at this point was that I need to support compound keys - keys that contained more than one simple value. But in the past, I was only able to specify properties for simple values. So I bit the bullet and enhanced that part of the system so that users could specify values for compound properties. Now, <a href="http://longevityframework.github.io/longevity/manual/key-values.html">key values</a> are case classes, and are easily retrieved from the persistent object on either side of the aggregative relationship. Here's an example where <span style="font-family: "courier new" , "courier" , monospace;">Users</span> have <span style="font-family: "courier new" , "courier" , monospace;">Username</span> as a key:</div>
<div>
<br /></div>
<div>
<pre class="highlight" style="background: rgb(255, 255, 255); border: 1px solid rgb(242, 242, 242); color: #222222; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; font-size: 13px; margin-bottom: 30px; overflow: auto; padding: 20px; text-shadow: none;"><code style="border: none; color: #2879d0; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; margin-bottom: 30px; padding: 0px;"><span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">Username</span><span class="o" style="color: #666666;">(</span><span class="n">username</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">)</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">extends</span> <span class="nc" style="color: blue;">KeyVal</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">User</span>, <span class="kt" style="color: #00bb00; font-weight: bold;">Username</span><span class="o" style="color: #666666;">](</span><span class="nc" style="color: blue;">User</span><span class="o" style="color: #666666;">.</span><span class="n">keys</span><span class="o" style="color: #666666;">.</span><span class="n">username</span><span class="o" style="color: #666666;">)</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">User</span><span class="o" style="color: #666666;">(</span>
<span class="n">username</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Username</span><span class="o" style="color: #666666;">,</span>
<span class="n">firstName</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">,</span>
<span class="n">lastName</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">)</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">extends</span> <span class="nc" style="color: blue;">Root</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">object</span> <span class="nc" style="color: blue;">User</span> <span class="k" style="color: #aa22ff; font-weight: bold;">extends</span> <span class="nc" style="color: blue;">RootType</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">User</span><span class="o" style="color: #666666;">]</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">object</span> <span class="nc" style="color: blue;">props</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">username</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="n">prop</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">Username</span><span class="o" style="color: #666666;">](</span><span class="s" style="color: #bb4444;">"username"</span><span class="o" style="color: #666666;">)</span>
<span class="o" style="color: #666666;">}</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">object</span> <span class="nc" style="color: blue;">keys</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">username</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="n">key</span><span class="o" style="color: #666666;">(</span><span class="n">props</span><span class="o" style="color: #666666;">.</span><span class="n">username</span><span class="o" style="color: #666666;">)</span>
<span class="o" style="color: #666666;">}</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">object</span> <span class="nc" style="color: blue;">indexes</span> <span class="o" style="color: #666666;">{</span>
<span class="o" style="color: #666666;">}</span>
<span class="o" style="color: #666666;">}</span></code></pre>
</div>
<div>
The contents of <span style="font-family: "courier new" , "courier" , monospace;">object User</span> may look a little strange, but it will quickly make sense if <a href="http://longevityframework.github.io/longevity/manual/ptype/">skim that chapter of the manual</a>. It's just declaring <span style="font-family: "courier new" , "courier" , monospace;">User.username</span> as a key.</div>
<div>
<br /></div>
<div>
Let's say a <span style="font-family: "courier new" , "courier" , monospace;">BlogPost</span> has multiple authors. In our <span style="font-family: "courier new" , "courier" , monospace;">BlogPost</span> case class, we simply specify the authors by <span style="font-family: "courier new" , "courier" , monospace;">Username</span>:</div>
<div>
<br /></div>
<div>
<pre class="highlight" style="background: rgb(255, 255, 255); border: 1px solid rgb(242, 242, 242); color: #222222; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; font-size: 13px; margin-bottom: 30px; overflow: auto; padding: 20px; text-shadow: none;"><code style="border: none; color: #2879d0; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; margin-bottom: 30px; padding: 0px;"><span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">BlogPost</span><span class="o" style="color: #666666;">(</span>
<span class="n">uri</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">BlogPostUri</span><span class="o" style="color: #666666;">,</span>
<span class="n">title</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">,</span>
<span class="n">content</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Markdown</span><span class="o" style="color: #666666;">,</span>
<span class="n">authors</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">List</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">Username</span><span class="o" style="color: #666666;">])</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">extends</span> <span class="nc" style="color: blue;">Root</span></code></pre>
</div>
<div>
And we easily can retrieve the first author from the post:</div>
<div>
<br /></div>
<div>
<pre class="highlight" style="background: rgb(255, 255, 255); border: 1px solid rgb(242, 242, 242); color: #222222; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; font-size: 13px; margin-bottom: 30px; overflow: auto; padding: 20px; text-shadow: none;"><code style="border: none; color: #2879d0; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; margin-bottom: 30px; padding: 0px;"><span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">authorUsername</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Username</span> <span class="o" style="color: #666666;">=</span> <span class="n">blogPost</span><span class="o" style="color: #666666;">.</span><span class="n">authors</span><span class="o" style="color: #666666;">.</span><span class="n">head</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">val</span> <span class="n">author</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">FPState</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">User</span><span class="o" style="color: #666666;">]</span> <span class="k" style="color: #aa22ff; font-weight: bold;">=</span> <span class="n">userRepo</span><span class="o" style="color: #666666;">.</span><span class="n">retrieveOne</span><span class="o" style="color: #666666;">(</span><span class="n">authorUsername</span><span class="o" style="color: #666666;">)</span></code></pre>
</div>
<div>
I hope you agree that this looks dead-simple and entirely intuitive.<br />
<br /></div>
<h3>
Closing Thoughts</h3>
<div>
I can't express in words how happy I am with the new longevity API. The improvements really show in the latest version of the <a href="http://longevityframework.org/getting-started/">Getting Started Guide</a>, which reads about ten times nicer than the previous version.</div>
<div>
<br /></div>
<div>
Over the past few months, longevity has really been firming up in a lot of ways. We've added some great new features, along with other API improvements in earlier releases. At this point, I feel fully confident in saying that the longevity API is solidifying, and the core API should remain stable from here on out. The <a href="http://longevityframework.org/manual/">user manual</a> is in great shape, and all the critical features are in place as well. Added features from here on out are gravy! Please give it a try. Please know that I am more than ready to help out any early adopters with getting started - whether that means just answering questions and giving advice, reprioritizing future longevity features that you want or need, or even partnering with you on establishing longevity in your project infrastructure.</div>
<div>
<br /></div>
<div>
Okay, back to working on my <a href="https://www.lightbend.com/activator">Lightbend Activator</a> tutorial! </div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-1604991544559079282016-05-26T11:56:00.000-04:002016-12-19T17:59:15.071-05:00Longevity Now Supports Querying with Akka StreamsI'm excited to announce <a href="http://longevityframework.org/">longevity</a> release 0.8, which features streaming queries with <a href="http://doc.akka.io/docs/akka/2.4.6/scala/stream/index.html">Akka Streams</a>! Prior to this release, longevity queries returned a list of results wrapped in a future, which was sort of only half-way reactive, and definitely wouldn't work in the face of query results that were too large to fit into memory. I was never particularly happy with this, but I had enough to do at the time with designing and implementing the <a href="http://longevityframework.github.io/longevity/manual/repo/query.html">query DSL</a>.<br />
<br />
<a name='more'></a><br />
There were a variety of choices for underlying technologies for streaming queries. I could have gone with <a href="https://github.com/ReactiveX/RxScala#rxscala-reactive-extensions-for-scala">RxScala</a>, <a href="https://github.com/ReactiveX/RxJava">RxJava</a>, or <a href="http://projectreactor.io/">Project Reactor</a>. One thing that all of these libraries have in common is they all implement the <a href="http://www.reactive-streams.org/">Reactive Streams API</a>. As I see it, the reactive streams project has two major goals. First, to provide a common underlying interface for streams libraries, so they can better interoperate. Second, to provide a library-level approach to handle back pressure, which is a means for the stream consumer to tell the producer that it needs to slow down, because the consumer can't keep up with the flow.<br />
<br />
Users no longer have to worry about throttling stream producers to prevent the consumer from getting overwhelmed, and eventually overrunning buffer. This is a good thing for two main reasons. It makes the library users' lives easier, and it also provides an elegant solution to a problem that is normally handled by <a href="https://en.wikipedia.org/wiki/Heuristic">heuristic</a> approaches that don't always give the best performance. As an example of what I mean, consider a scenario where the stream producer is going too fast for the consumer. The most obvious approach to solve this problem is to throttle, or slow down, the producer. So the programmer chooses some throttle technique - for instance, forcing a 100 millisecond wait between elements produced. This prevents the consumer from being overwhelmed in most cases, and can be tuned to be as performant as possible <i>in whatever testing environment is being used</i>. But it does not prevent the consumer from being overwhelmed in all circumstances, and in some environments, the consumer may be able to handle more throughput. With reactive streams, the throttling is done dynamically, to match the capacity of the consumer.<br />
<br />
I chose Akka Streams over the alternatives for a handful of reasons. For one, I mildly prefer to provide a Scala-based API for my users. I also expect better integration between <a href="http://akka.io/">Akka</a> and the Scala language and library, due to the close relationship between the Akka and Scala teams. But more importantly, longevity is a tool designed for enterprise application development, and I expect many of my users will already be using Akka for other aspects of their development. This will help them limit the number of dependencies their projects have, and it will make for easier integration with other parts of their applications.<br />
<br />
I also foresee more integration points with Akka in future longevity development. These features are far off, and haven't even really made it on my <a href="https://www.pivotaltracker.com/n/projects/1231978">planning board</a> yet. I'm really focusing on getting the core persistence story done well first. Once that is in place, there is room for further application support. For instance, automatically generated REST APIs for longevity persistence. I would probably go with <a href="http://doc.akka.io/docs/akka/2.4.6/scala/http/">Akka HTTP</a> for that. I've also tossed around the idea of having an actor-based API for the longevity repositories. Finally, I would like to add support for popular DDD approaches such as <a href="http://martinfowler.com/bliki/CQRS.html">CQRS</a>, <a href="https://en.wikipedia.org/wiki/Event-driven_architecture">event driven architecture</a> and <a href="http://martinfowler.com/eaaDev/EventSourcing.html">event sourcing</a>. I would want to take a close look at <a href="http://doc.akka.io/docs/akka/current/scala/persistence.html">Akka Persistence</a> here, finding possible integration points with that project, or perhaps borrowing ideas from it.<br />
<br />
In any event, I am so glad that this feature is complete, and that it was so easy to integrate with Akka Streams! I am really looking forward to my next longevity task, which is to write a full-fledged getting started guide. I put together a somewhat hasty <a href="http://longevityframework.org/getting-started">quick start guide</a> when I was doing the initial draft of the <a href="http://longevityframework.org/manual/">user manual</a>. When writing the user manual, I realized that while it was a very important and useful document to have, I needed a better tool to help people get started with longevity. But the first draft of the manual was a major piece of work, and I didn't have time to write a proper getting started guide at the time. Now is the time! I'm excited to work on a sample application to use in the getting started guide, and to get this important piece of user support material out there. I'm also looking forward to taking a look at <a href="https://www.lightbend.com/activator/download">Lightbend Activator</a> again. I dabbled in it on <a href="https://github.com/sullivan-/congeal">another project</a> a couple years back, and I'm looking forward to seeing how it's evolved since then.<br />
<br />
For more information on streaming queries in longevity, please see the <a href="http://longevityframework.github.io/longevity/manual/repo/stream.html">section on <span style="font-family: "courier new" , "courier" , monospace;">Repo.streamByQuery</span> in the user manual</a>.<br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5659866753486893848.post-12321139670641881152016-05-19T11:53:00.000-04:002016-12-19T18:01:12.389-05:00Longevity Release 0.7.0 - Entity Polymorphism<a href="http://longevityframework.org/">Longevity</a> release 0.7.0 is out! The major focus of this release is support for entity polymorphism - that is, subtyping - in your domain model. This is a critical usability feature. It's also hopefully the last of what I might call "core features": those features that require me to go back and rework the core code of the longevity engine, consequently causing major ripple effects throughout the project. So this is a big release for me! It's a critical feature, and hopefully the last feature in a while that takes me a month to complete!<br />
<br />
From the outside looking in, it may not seem like supporting subtyping in your domain model should be such a big deal. To understand why this is, I need to step back and discuss some of the key principles and design decisions I made in this project.<br />
<br />
<a name='more'></a>Part of what makes database applications hard is the impedance mismatch between the way we model domain in the database, and the way we model domain in our software. This is a rather large problem, with many aspects. I want to focus on a single aspect of this problem here: what I might call the irregularity of data structures in modern programming languages.<br />
<br />
When we implement our domain model in our software, of course we want to avail ourselves of any and all programming language features at our disposal. It's not natural to stop and ask questions like, "Should I really be using this data type here? Is this going to cause problems when I go to persist it?" And it is entirely appropriate that such questions are not natural! We should not be worrying about a thousand nagging persistence concerns while designing our domain. Separation of concerns is a critical activity in software engineering, and even more so when it comes to our domain!<br />
<br />
But using whatever language features we please in our domain model leads to other problems. This is because, in modern programming languages, there is no generic way to process our objects, or data. There is no standard serialization algorithm, because there is no standard traversal algorithm. There is no standard way to distinguish between data that is fundamental to the object - i.e., data that we need to reconstitute the object - and data that is tertiary, such as computed fields. Indeed, in Scala, there is not even a clear dividing line between data and methods, as defs can be overridden with vals, and lazy vals confuse the picture even further. And there is no standard way to reconstitute data from a serialized form, as there are any number of approaches to creating the object in the first place.<br />
<br />
If we do not restrict the kinds of language features that we use in our domain model, we cannot make use of any generic, third-party tool for doing our persistence. The end result of this is home-grown persistence layers all over the place. At times it seems there is one per database application. There is nothing wrong per se with a home-grown persistence layer, but really, that is <i>a lot</i> of code to write and maintain. And it's not easy code to write! There are a lot of tricky aspects to it. And as your domain model continues to evolve, you find yourself using new language features in your domain that your home-grown repositories are not prepared to handle. At this point, you have a choice: either go back and add features to repositories to support your new situation, or go back and rework your domain model to not use these features. In the latter scenario, we end up defeating the purpose of growing our own persistence layer in the first place: the supposed freedom to design our domain using all the language features at our disposal.<br />
<br />
Before I continue I would like to point out for clarity: If you are using a database driver, and your program is more than a few hundred lines long, then yes, you are writing your own persistence layer!<br />
<br />
Third-party tools that manage persistence concerns for you have a particularly difficult job, because they have to balance between limiting the language features you can use in your domain model, and compiling reams and reams of code to handle every possible thing a user might throw at them. Take JPA for example. JPA tries to be as flexible as possible for it's users, and part of the way it accomplishes this is by using annotations to tell JPA how to interpret your entities. So while part of the burden of managing your persistence is shunted back to the user in the the form of extra-lingual but in-line configuration, the fact is that JPA still places tremendous limits on the language features you can use in your entities. Take one simple example. Want to use immutable collections in your domain model? You can't do that with JPA!<br />
<br />
When I set off to write longevity, I knew I was going to need to handle data structures in a generic way, and there was no way that I would be able to support every possible data structure concept that you can employ in Scala. So my approach was to provide a limited set of language features you can use in your domain model. I felt from the start that this would be a reasonable approach, because thankfully, Scala provides us with case classes, which are not only really cool and easy to use, they also provide a great deal of regularity and make things easy to treat in a general way.<br />
<br />
Being a mathematically minded person (maybe some functionalists would disagree with that!), I started with our base cases. These are the leaf-level data elements that I would support. I chose a small but comprehensive set of basic types: Boolean, Char, Double, Float, Int, Long, String, and Joda DateTime.<br />
<br />
Once I had my base cases, I wanted to support some collections. Again, thankfully, Scala provides a rich set of collection types that are standard, well-liked, and well-used. I initially supported Option, Set, and List. I would like to support more collection types such as Pair (ahem, Tuple2) and Map, and perhaps some other kinds of Seq. But Option, Set, and List should support most users' collection needs.<br />
<br />
After that, of course, we need to support case classes. I had to put some minor limitations on the case classes I support, such as a single-parameter list, because I need to be able to produce as well as process data from your domain model.<br />
<br />
So, that's a somewhat limited set there, but it covers most of what you might want to do in your domain model, and it does it in a way that should not be repulsive to you. One of the major holes in my initial implementation was, you guessed it, entity polymorphism. Better known as subtyping. I didn't support cases like this:<br />
<br />
<pre class="highlight" style="background: rgb(255, 255, 255); border: 1px solid rgb(242, 242, 242); color: #222222; font-family: Monaco, 'Bitstream Vera Sans Mono', 'Lucida Console', Terminal, monospace; font-size: 13px; margin-bottom: 30px; overflow: auto; padding: 20px; text-shadow: none;"><code style="border: none; color: #2879d0; font-family: Monaco, 'Bitstream Vera Sans Mono', 'Lucida Console', Terminal, monospace; margin-bottom: 30px; padding: 0px;"><span class="k" style="color: #aa22ff; font-weight: bold;">trait</span> <span class="nc" style="color: blue;">Avatar</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">def</span> <span class="n">draw</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Image</span>
<span class="o" style="color: #666666;">}</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">PixelatedAvatar</span><span class="o" style="color: #666666;">(</span><span class="n">pixels</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Set</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">Pixel</span><span class="o" style="color: #666666;">])</span> <span class="k" style="color: #aa22ff; font-weight: bold;">extends</span> <span class="nc" style="color: blue;">Avatar</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">def</span> <span class="n">draw</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Image</span> <span class="o" style="color: #666666;">=</span> <span class="n">pixels</span><span class="o" style="color: #666666;">.</span><span class="n">foldLeft</span><span class="o" style="color: #666666;">(</span><span class="nc" style="color: blue;">Image</span><span class="o" style="color: #666666;">.</span><span class="n">empty</span><span class="o" style="color: #666666;">)(</span><span class="n">addPixelToImage</span><span class="o" style="color: #666666;">)</span>
<span class="o" style="color: #666666;">}</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">VectorAvatar</span><span class="o" style="color: #666666;">(</span><span class="n">vectors</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">List</span><span class="o" style="color: #666666;">[</span><span class="kt" style="color: #00bb00; font-weight: bold;">Vector</span><span class="o" style="color: #666666;">])</span> <span class="k" style="color: #aa22ff; font-weight: bold;">extends</span> <span class="nc" style="color: blue;">Avatar</span> <span class="o" style="color: #666666;">{</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">def</span> <span class="n">draw</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Image</span> <span class="o" style="color: #666666;">=</span> <span class="n">vectors</span><span class="o" style="color: #666666;">.</span><span class="n">foldLeft</span><span class="o" style="color: #666666;">(</span><span class="nc" style="color: blue;">Image</span><span class="o" style="color: #666666;">.</span><span class="n">empty</span><span class="o" style="color: #666666;">)(</span><span class="n">addVectorToImage</span><span class="o" style="color: #666666;">)</span>
<span class="o" style="color: #666666;">}</span>
<span class="k" style="color: #aa22ff; font-weight: bold;">case</span> <span class="k" style="color: #aa22ff; font-weight: bold;">class</span> <span class="nc" style="color: blue;">User</span><span class="o" style="color: #666666;">(</span>
<span class="n">username</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">String</span><span class="o" style="color: #666666;">,</span>
<span class="n">email</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Email</span><span class="o" style="color: #666666;">,</span>
<span class="n">avatar</span><span class="k" style="color: #aa22ff; font-weight: bold;">:</span> <span class="kt" style="color: #00bb00; font-weight: bold;">Avatar</span><span class="o" style="color: #666666;">)</span></code></pre>
So yeah, that's what this release is mainly about: supporting things like that in your domain. If you are interested in reading more about it, check out the <a href="http://longevityframework.org/manual/poly/">chapter on polymorphic entities</a> in the <a href="http://longevityframework.org/manual">user manual</a>. One thing that is cool about the way I did this is, if you use subtyping in your persistent entities, then you get repositories for you parent type, as well as all of your realization types. All the repositories use the same backing table/collection, so you can easily switch from, say, working with Users, to working with Members or NonMembers. You can read about this in the user manual well, in the <a href="http://longevityframework.org/manual/repo/poly.html">section on polymorphic repositories</a>.<br />
<br />
<br />
<br />Unknownnoreply@blogger.com0