0.4.5 • Published 3 years ago

ts-simple-interfaces v0.4.5

Weekly downloads
5
License
MIT
Repository
github
Last release
3 years ago

Simple Interfaces for Typescript

NOTE: This library is experimental right now and in active alpha development. While you may feel free to use it, it is expected to change considerably.

NOTE: As of v0.5.0 this project is now mantained by Wymp and the package is published on github. Please use @wymp/ts-simple-interfaces for future versions of this package. (This will require setting up an .npmrc file at some level with the following line: @wymp:registry=https://npm.pkg.github.com/wymp.)

TL;DR

The "Simple" suite of packages should be considered a collection of interfaces (defined in this package) along with a collection of concrete implementations (defined in other packages prefaced with simple-) that allow you to write code that is highly portable and highly testable.

The idea is that your code should always use the simple interfaces, for function argument types, and that you should then pass in the concrete implementations that you wish to use.

For example, your application may require an HTTP client, like Axios or Request Promise Native. To set your application up, you would type the variable holding the client as SimpleHttpClientInterface and then set it to a concrete implementation, such as SimpleHttpClientAxios from the simple-http-client-axios package:

// src/MyClient.ts
import { SimpleHttpClientInterface } from "ts-simple-interfaces";
import { SomeData } from "./Types";

export class MyClient {
  public constructor(protected httpClient: SimpleHttpClientInterface) { }

  public getSomeData() {
    return this.httpClient.request<SomeData>({
      //...
    });
  }
}

// --------------------------------------------------------------------

// src/index.ts
import { MyClient } from "./MyClient";
import { SimpleHttpClientAxios } from "simple-http-client-axios";

const axios = new SimpleHttpClientAxios();
const myClient = new MyClient(axios);

const data = myClient.getSomeData();
// ....

Doing this means that if for any reason in the future it Axios goes out of production or becomes problematic, you can easily switch to a different implementation with minimal adjustments.

Note that the idea is not necessarily that there be no adjustments. Individual implementations have varying features that you may want to use. However, sticking to Simple-conformant implementations not only minimizes the adjustments you have to make when migrating, but it also increases your options for testing, as other people can focus on building robust, widely applicable test implementations that conform to the general Simple interfaces and can be dropped in with no modifications, or at least only minor ones.

Longer Overview

This library attempts to define a set of simplified interfaces for typescript that are compatible with common tools in the ecosystem. Most of these tools are I/O-based, such as pub/subs (amqp), relational databases (mysql, postgres), key-value stores (redis), and loggers (winston), but technically any complex tool is fair game for a simplified interface.

The problem is that many of these tools have unnecessarily complex native interfaces that make them problematic for testing and portability. For example, Winston is a wonderful logger, but its LoggerInterface defines a bunch of methods that are not very useful to the average user, and furthermore extends NodeJSStream.Transport, yielding a final interface that defines over 40 methods that have little to do with logging messages. Thus, incorporating these interfaces into your code unnecessarily expands the contracts that your code uses, making your applications less flexible, more difficult to test, and more difficult to migrate to new technologies.

Another issue is that Winston's LoggerInterface is defined in the library itself, meaning that if you want to use that LoggerInterface, you have to depend on the whole Winston package. This isn't technically a huge deal, but it also tends to hamper the responsible specification of general interfaces that are independent of any single project.

TOC for Simple Suite of Packages

As mentioned in the TL;DR, the "simple" concept is actually a collection of repositories/packages. For each interface defined, the goal is to create one or more "official" concrete implementations based on current popular libraries.

Because the Simple suite is still very much a work in progress, very few concrete implementations have been defined. However, as the interfaces stabilize and are used in production environments, the hope is to deliver more. Feel free to let me know about any concrete implementations that spring up in the wild....

Following is a list of implementations by interface:

SimpleHttpClientInterface

SimpleHttpRequestHandlerInterface

SimpleLoggerInterface

SimplePubSubInterface

SimpleSqlDbInterface

Testing

One of the advantages of referencing these interfaces in your code is that you can use potentially powerful pre-built testing mocks/spies/stubs that conform to them. At the time of this writing, these are being developed here.

For example, you can instantiate a SimpleMockSqlDb that implements SimpleSqlDbInterface and gives you the ability to easily analyze query requests and define responses.

Usage

To use this library, just list it as a dependency1 and then define classes that implement these interfaces. For example:

// src/MyDatasource.ts
import { SimpleDatasourceInterface } from "ts-simple-interfaces";

export class MyDatasource implements SimpleDatasourceInterface {
  ....
}

API

The best way to understand the API is by just looking at the declarations file here.

Footnotes

1 Note that this library should be included as a proper dependency, not a dev dependency, because it may define enums that actually compile down to javascript that your project uses at runtime.